diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9b7a7b939..d72d33ac4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -23,7 +23,7 @@ jobs: run: | cargo install cargo-ndk rustup target add x86_64-unknown-linux-gnu - sudo apt install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm + sudo apt install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm sudo apt install -y debhelper libclang-dev cargo rustc opencl-headers libssl-dev ocl-icd-opencl-dev sudo apt install -y libc6-dev-i386 sudo apt install -y build-essential cmake git libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev pkg-config llvm @@ -50,48 +50,36 @@ jobs: $encodedBytes = [System.Convert]::FromBase64String($env:CHANGE_NOW); Set-Content $secretFileExchange -Value $encodedBytes -AsByteStream; $secretFileExchangeHash = Get-FileHash $secretFileExchange; - Write-Output "::set-output name=SECRET_FILE_EXCHANGE::$secretFileExchange"; - Write-Output "::set-output name=SECRET_FILE_EXCHANGE_HASH::$($secretFileExchangeHash.Hash)"; Write-Output "Secret file $secretFileExchange has hash $($secretFileExchangeHash.Hash)"; $secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart"; $encodedBytes = [System.Convert]::FromBase64String($env:BITCOIN_TEST); Set-Content $secretFileBitcoin -Value $encodedBytes -AsByteStream; $secretFileBitcoinHash = Get-FileHash $secretFileBitcoin; - Write-Output "::set-output name=SECRET_FILE_BITCOIN::$secretFileBitcoin"; - Write-Output "::set-output name=SECRET_FILE_BITCOIN_HASH::$($secretFileBitcoinHash.Hash)"; Write-Output "Secret file $secretFileBitcoin has hash $($secretFileBitcoinHash.Hash)"; $secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart"; $encodedBytes = [System.Convert]::FromBase64String($env:DOGECOIN_TEST); Set-Content $secretFileDogecoin -Value $encodedBytes -AsByteStream; $secretFileDogecoinHash = Get-FileHash $secretFileDogecoin; - Write-Output "::set-output name=SECRET_FILE_DOGECOIN::$secretFileDogecoin"; - Write-Output "::set-output name=SECRET_FILE_DOGECOIN_HASH::$($secretFileDogecoinHash.Hash)"; Write-Output "Secret file $secretFileDogecoin has hash $($secretFileDogecoinHash.Hash)"; $secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart"; $encodedBytes = [System.Convert]::FromBase64String($env:FIRO_TEST); Set-Content $secretFileFiro -Value $encodedBytes -AsByteStream; $secretFileFiroHash = Get-FileHash $secretFileFiro; - Write-Output "::set-output name=SECRET_FILE_FIRO::$secretFileFiro"; - Write-Output "::set-output name=SECRET_FILE_FIRO_HASH::$($secretFileFiroHash.Hash)"; Write-Output "Secret file $secretFileFiro has hash $($secretFileFiroHash.Hash)"; $secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart"; $encodedBytes = [System.Convert]::FromBase64String($env:BITCOINCASH_TEST); Set-Content $secretFileBitcoinCash -Value $encodedBytes -AsByteStream; $secretFileBitcoinCashHash = Get-FileHash $secretFileBitcoinCash; - Write-Output "::set-output name=SECRET_FILE_BITCOINCASH::$secretFileBitcoinCash"; - Write-Output "::set-output name=SECRET_FILE_BITCOINCASH_HASH::$($secretFileBitcoinCashHash.Hash)"; Write-Output "Secret file $secretFileBitcoinCash has hash $($secretFileBitcoinCashHash.Hash)"; $secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart"; $encodedBytes = [System.Convert]::FromBase64String($env:NAMECOIN_TEST); Set-Content $secretFileNamecoin -Value $encodedBytes -AsByteStream; $secretFileNamecoinHash = Get-FileHash $secretFileNamecoin; - Write-Output "::set-output name=SECRET_FILE_NAMECOIN::$secretFileNamecoin"; - Write-Output "::set-output name=SECRET_FILE_NAMECOIN_HASH::$($secretFileNamecoinHash.Hash)"; Write-Output "Secret file $secretFileNamecoin has hash $($secretFileNamecoinHash.Hash)"; shell: pwsh @@ -114,18 +102,18 @@ jobs: file: coverage/lcov.info - name: Delete temp files run: | - Remove-Item -Path $env:CHANGE_NOW; - Remove-Item -Path $env:BITCOIN_TEST; - Remove-Item -Path $env:DOGECOIN_TEST; - Remove-Item -Path $env:FIRO_TEST; - Remove-Item -Path $env:BITCOINCASH_TEST; - Remove-Item -Path $env:NAMECOIN_TEST; + $secretFileExchange = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "lib/external_api_keys.dart"; + $secretFileBitcoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoin/bitcoin_wallet_test_parameters.dart"; + $secretFileDogecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/dogecoin/dogecoin_wallet_test_parameters.dart"; + $secretFileFiro = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/firo/firo_wallet_test_parameters.dart"; + $secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart"; + $secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart"; + + Remove-Item -Path $secretFileExchange; + Remove-Item -Path $secretFileBitcoin; + Remove-Item -Path $secretFileDogecoin; + Remove-Item -Path $secretFileFiro; + Remove-Item -Path $secretFileBitcoinCash; + Remove-Item -Path $secretFileNamecoin; shell: pwsh if: always() - env: - CHANGE_NOW: ${{ steps.secret-file1.outputs.SECRET_FILE_EXCHANGE }} - BITCOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOIN }} - DOGECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_DOGECOIN }} - FIRO_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_FIRO }} - BITCOINCASH_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOINCASH }} - NAMECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_NAMECOIN }} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cbdd1b7c7..318694397 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ diff --git a/android/build.gradle b/android/build.gradle index c9369bc69..b309554fd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.20' repositories { google() jcenter() diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 90f271dfd..562c5e444 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7a18ec36d..4b7b1058d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -454,7 +454,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 4DQKUWSG6C; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -508,7 +508,7 @@ "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); - MARKETING_VERSION = 1.5.8; + MARKETING_VERSION = 1.5.9; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -641,7 +641,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 4DQKUWSG6C; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -695,7 +695,7 @@ "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); - MARKETING_VERSION = 1.5.8; + MARKETING_VERSION = 1.5.9; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -720,7 +720,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 78; + CURRENT_PROJECT_VERSION = 79; DEVELOPMENT_TEAM = 4DQKUWSG6C; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -774,7 +774,7 @@ "$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**", "$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs", ); - MARKETING_VERSION = 1.5.8; + MARKETING_VERSION = 1.5.9; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 7a90c2343..2f7916d9e 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -1,9 +1,12 @@ +import 'dart:convert'; + import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/prefs.dart'; +import 'package:string_validator/string_validator.dart'; class CachedElectrumX { final ElectrumX? electrumXClient; @@ -94,10 +97,32 @@ class CachedElectrumX { // update set with new data if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) { - set["setHash"] = newSet["setHash"]; - set["blockHash"] = newSet["blockHash"]; + set["setHash"] = !isHexadecimal(newSet["setHash"] as String) + ? base64ToReverseHex(newSet["setHash"] as String) + : newSet["setHash"]; + set["blockHash"] = !isHexadecimal(newSet["blockHash"] as String) + ? base64ToHex(newSet["blockHash"] as String) + : newSet["blockHash"]; for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) { - set["coins"].insert(0, newSet["coins"][i]); + dynamic newCoin = newSet["coins"][i]; + List translatedCoin = []; + translatedCoin.add(!isHexadecimal(newCoin[0] as String) + ? base64ToHex(newCoin[0] as String) + : newCoin[0]); + translatedCoin.add(!isHexadecimal(newCoin[1] as String) + ? base64ToReverseHex(newCoin[1] as String) + : newCoin[1]); + try { + translatedCoin.add(!isHexadecimal(newCoin[2] as String) + ? base64ToHex(newCoin[2] as String) + : newCoin[2]); + } catch (e, s) { + translatedCoin.add(newCoin[2]); + } + translatedCoin.add(!isHexadecimal(newCoin[3] as String) + ? base64ToReverseHex(newCoin[3] as String) + : newCoin[3]); + set["coins"].insert(0, translatedCoin); } // save set to db await DB.instance.put( @@ -118,6 +143,17 @@ class CachedElectrumX { } } + String base64ToHex(String source) => + base64Decode(LineSplitter.split(source).join()) + .map((e) => e.toRadixString(16).padLeft(2, '0')) + .join(); + + String base64ToReverseHex(String source) => + base64Decode(LineSplitter.split(source).join()) + .reversed + .map((e) => e.toRadixString(16).padLeft(2, '0')) + .join(); + /// Call electrumx getTransaction on a per coin basis, storing the result in local db if not already there. /// /// ElectrumX api only called if the tx does not exist in local db @@ -189,7 +225,15 @@ class CachedElectrumX { ); final serials = await client.getUsedCoinSerials(startNumber: startNumber); - cachedSerials.addAll(serials["serials"] as List); + List newSerials = []; + for (var element in (serials["serials"] as List)) { + if (!isHexadecimal(element as String)) { + newSerials.add(base64ToHex(element)); + } else { + newSerials.add(element); + } + } + cachedSerials.addAll(newSerials); await DB.instance.put( boxName: DB.instance.boxNameUsedSerialsCache(coin: coin), diff --git a/lib/hive/db.dart b/lib/hive/db.dart index 062d029a5..e1232696b 100644 --- a/lib/hive/db.dart +++ b/lib/hive/db.dart @@ -9,6 +9,7 @@ import 'package:stackwallet/models/node_model.dart'; import 'package:stackwallet/models/notification_model.dart'; import 'package:stackwallet/models/trade_wallet_lookup.dart'; import 'package:stackwallet/services/wallets_service.dart'; +import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; @@ -142,6 +143,17 @@ class DB { _loadSharedCoinCacheBoxes(), ]); _initialized = true; + + try { + if (_boxPrefs.get("familiarity") == null) { + await _boxPrefs.put("familiarity", 0); + } + int count = _boxPrefs.get("familiarity") as int; + await _boxPrefs.put("familiarity", count + 1); + Constants.exchangeForExperiencedUsers(count + 1); + } catch (e, s) { + print("$e $s"); + } } } diff --git a/lib/main.dart b/lib/main.dart index e1297cc5a..aa2155478 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -146,7 +146,12 @@ void main() async { boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ?? 0; if (dbVersion < Constants.currentHiveDbVersion) { - await DbVersionMigrator().migrate(dbVersion); + try { + await DbVersionMigrator().migrate(dbVersion); + } catch (e, s) { + Logging.instance.log("Cannot migrate database\n$e $s", + level: LogLevel.Error, printFullLength: true); + } } monero.onStartup(); @@ -234,7 +239,9 @@ class _MaterialAppWithThemeState extends ConsumerState // unawaited(_nodeService.updateCommunityNodes()); // run without awaiting - if (Constants.enableExchange && _prefs.externalCalls) { + if (Constants.enableExchange && + _prefs.externalCalls && + await _prefs.isExternalCallsSet()) { unawaited(ExchangeDataLoadingService().loadAll(ref)); } diff --git a/lib/models/exchange/exchange_form_state.dart b/lib/models/exchange/exchange_form_state.dart index 2e237dd1c..62509f7bb 100644 --- a/lib/models/exchange/exchange_form_state.dart +++ b/lib/models/exchange/exchange_form_state.dart @@ -276,6 +276,12 @@ class ExchangeFormState extends ChangeNotifier { void _onExchangeRateTypeChanged() { print("_onExchangeRateTypeChanged"); + updateRanges(shouldNotifyListeners: true).then( + (_) => updateEstimate( + shouldNotifyListeners: true, + reversed: reversed, + ), + ); } void _onExchangeTypeChanged() { diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 0edc110be..76e74fa14 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -144,6 +144,8 @@ class _RestoreOptionsViewState extends ConsumerState { Future chooseDate() async { final height = MediaQuery.of(context).size.height; + final fetchedColor = + Theme.of(context).extension()!.accentColorDark; // check and hide keyboard if (FocusScope.of(context).hasFocus) { FocusScope.of(context).unfocus(); @@ -155,8 +157,7 @@ class _RestoreOptionsViewState extends ConsumerState { initialDate: DateTime.now(), height: height * 0.5, theme: ThemeData( - primarySwatch: Util.createMaterialColor( - Theme.of(context).extension()!.accentColorDark), + primarySwatch: Util.createMaterialColor(fetchedColor), ), //TODO pick a better initial date // 2007 chosen as that is just before bitcoin launched @@ -272,6 +273,7 @@ class _RestoreOptionsViewState extends ConsumerState { // if (!isDesktop) RestoreFromDatePicker( onTap: chooseDate, + controller: _dateController, ), // if (isDesktop) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart index 112d50428..803e9b03b 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart @@ -7,10 +7,14 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; class RestoreFromDatePicker extends StatefulWidget { - const RestoreFromDatePicker({Key? key, required this.onTap}) - : super(key: key); + const RestoreFromDatePicker({ + Key? key, + required this.onTap, + required this.controller, + }) : super(key: key); final VoidCallback onTap; + final TextEditingController controller; @override State createState() => _RestoreFromDatePickerState(); @@ -23,17 +27,11 @@ class _RestoreFromDatePickerState extends State { @override void initState() { onTap = widget.onTap; - _dateController = TextEditingController(); + _dateController = widget.controller; super.initState(); } - @override - void dispose() { - _dateController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return Container( diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart index e5c442173..a3aa925a0 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/debug_view.dart @@ -1,11 +1,15 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:event_bus/event_bus.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:stackwallet/models/isar/models/log.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; @@ -24,6 +28,13 @@ import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; +import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS; +import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS; +import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS; + +import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart'; + +import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/util.dart'; @@ -272,21 +283,77 @@ class _DebugViewState extends ConsumerState { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // BlueTextButton( - // text: ref.watch(debugServiceProvider - // .select((value) => value.isPaused)) - // ? "Unpause" - // : "Pause", - // onTap: () { - // ref - // .read(debugServiceProvider) - // .togglePauseUiUpdates(); - // }, - // ), + BlueTextButton( + text: "Save Debug Info to clipboard", + onTap: () async { + try { + final packageInfo = + await PackageInfo.fromPlatform(); + final version = packageInfo.version; + final build = packageInfo.buildNumber; + final signature = packageInfo.buildSignature; + final appName = packageInfo.appName; + String firoCommit = + FIRO_VERSIONS.getPluginVersion(); + String epicCashCommit = + EPIC_VERSIONS.getPluginVersion(); + String moneroCommit = + MONERO_VERSIONS.getPluginVersion(); + DeviceInfoPlugin deviceInfoPlugin = + DeviceInfoPlugin(); + final deviceInfo = + await deviceInfoPlugin.deviceInfo; + var deviceInfoMap = deviceInfo.toMap(); + deviceInfoMap.remove("systemFeatures"); + + final logs = filtered( + ref.watch(debugServiceProvider.select( + (value) => value.recentLogs)), + _searchTerm) + .reversed + .toList(growable: false); + List errorLogs = []; + for (var log in logs) { + if (log.logLevel == LogLevel.Error || + log.logLevel == LogLevel.Fatal) { + errorLogs.add( + "${log.logLevel}: ${log.message}"); + } + } + + final finalDebugMap = { + "version": version, + "build": build, + "signature": signature, + "appName": appName, + "firoCommit": firoCommit, + "epicCashCommit": epicCashCommit, + "moneroCommit": moneroCommit, + "deviceInfoMap": deviceInfoMap, + "errorLogs": errorLogs, + }; + Logging.instance.log( + json.encode(finalDebugMap), + level: LogLevel.Info, + printFullLength: true); + const ClipboardInterface clipboard = + ClipboardWrapper(); + await clipboard.setData( + ClipboardData( + text: json.encode(finalDebugMap)), + ); + } catch (e, s) { + Logging.instance + .log("$e $s", level: LogLevel.Error); + } + }, + ), const Spacer(), BlueTextButton( text: "Save logs to file", onTap: () async { + final systemfile = StackFileSystem(); + await systemfile.prepareStorage(); Directory rootPath = (await getApplicationDocumentsDirectory()); @@ -313,8 +380,9 @@ class _DebugViewState extends ConsumerState { } else { path = await FilePicker.platform .getDirectoryPath( - dialogTitle: "Choose Backup location", - initialDirectory: dir.path, + dialogTitle: "Choose Log Save Location", + initialDirectory: + systemfile.startPath!.path, lockParentWindow: true, ); } @@ -336,9 +404,17 @@ class _DebugViewState extends ConsumerState { ), )); - final filename = await ref - .read(debugServiceProvider) - .exportToFile(path, eventBus); + bool logssaved = true; + var filename; + try { + filename = await ref + .read(debugServiceProvider) + .exportToFile(path, eventBus); + } catch (e, s) { + logssaved = false; + Logging.instance + .log("$e $s", level: LogLevel.Error); + } shouldPop = true; @@ -350,7 +426,9 @@ class _DebugViewState extends ConsumerState { showDialog( context: context, builder: (context) => StackOkDialog( - title: "Logs saved to", + title: logssaved + ? "Logs saved to" + : "Error Saving Logs", message: "${path!}/$filename", ), ), @@ -360,7 +438,9 @@ class _DebugViewState extends ConsumerState { showFloatingFlushBar( type: FlushBarType.info, context: context, - message: 'Logs file saved', + message: logssaved + ? 'Logs file saved' + : "Error Saving Logs", ), ); } diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart index 84dd2b5d9..6d803eb6d 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart @@ -687,27 +687,14 @@ abstract class SWB { uiState?.walletStates = walletStates; List> restoreStatuses = []; - final List> firoWallets = []; - final List> firoTestnetWallets = []; - final List> epicCashWallets = []; // start restoring wallets for (final tuple in managers) { // check if cancel was requested and restore previous state if (_checkShouldCancel(preRestoreState)) { return false; } - - if (tuple.item2.coin == Coin.firoTestNet) { - firoTestnetWallets.add(tuple); - continue; - } else if (tuple.item2.coin == Coin.firo) { - firoWallets.add(tuple); - continue; - } else if (tuple.item2.coin == Coin.epicCash) { - epicCashWallets.add(tuple); - continue; - } - restoreStatuses.add(asyncRestore(tuple, uiState, walletsService)); + final bools = await asyncRestore(tuple, uiState, walletsService); + restoreStatuses.add(Future(() => bools)); } // check if cancel was requested and restore previous state @@ -715,153 +702,6 @@ abstract class SWB { return false; } - if (firoTestnetWallets.isNotEmpty) { - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - for (final wallet in firoTestnetWallets) { - uiState?.update( - walletId: wallet.item2.walletId, - restoringStatus: StackRestoringStatus.restoring, - ); - } - // try using node from backup first - NodeModel node = nodeService.getPrimaryNodeFor(coin: Coin.firoTestNet) ?? - DefaultNodes.getNodeFor(Coin.firoTestNet); - - final electrumxNode = ElectrumXNode( - address: node.host, - port: node.port, - name: node.name, - id: node.id, - useSSL: node.useSSL, - ); - - final failovers = nodeService.failoverNodesFor(coin: Coin.firoTestNet); - - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - final cachedClient = CachedElectrumX.from( - node: electrumxNode, - prefs: _prefs, - failovers: failovers - .map( - (e) => ElectrumXNode( - address: e.host, - port: e.port, - name: e.name, - id: e.id, - useSSL: e.useSSL, - ), - ) - .toList(), - ); - - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - // Anonymity Set often fails when gathering from the server - const int maxTries = 5; - for (int j = 0; j < maxTries; j++) { - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - try { - await cachedClient.getAnonymitySet( - groupId: "1", - coin: Coin.firoTestNet, - ); - break; - } catch (_) { - continue; - } - } - } - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - if (firoWallets.isNotEmpty) { - for (final wallet in firoWallets) { - uiState?.update( - walletId: wallet.item2.walletId, - restoringStatus: StackRestoringStatus.restoring, - ); - } - - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - // try using node from backup first - NodeModel node = nodeService.getPrimaryNodeFor(coin: Coin.firo) ?? - DefaultNodes.getNodeFor(Coin.firo); - - final electrumxNode = ElectrumXNode( - address: node.host, - port: node.port, - name: node.name, - id: node.id, - useSSL: node.useSSL, - ); - final failovers = nodeService.failoverNodesFor(coin: Coin.firoTestNet); - final cachedClient = CachedElectrumX.from( - node: electrumxNode, - prefs: _prefs, - failovers: failovers - .map( - (e) => ElectrumXNode( - address: e.host, - port: e.port, - name: e.name, - id: e.id, - useSSL: e.useSSL, - ), - ) - .toList()); - - // Anonymity Set often fails when gathering from the server - const int maxTries = 5; - for (int j = 0; j < maxTries; j++) { - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - try { - await cachedClient.getAnonymitySet( - groupId: "1", - coin: Coin.firo, - ); - break; - } catch (_) { - continue; - } - } - } - - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - - for (final tuple in firoTestnetWallets) { - restoreStatuses.add(asyncRestore(tuple, uiState, walletsService)); - } - for (final tuple in firoWallets) { - restoreStatuses.add(asyncRestore(tuple, uiState, walletsService)); - } - for (Future status in restoreStatuses) { // check if cancel was requested and restore previous state if (_checkShouldCancel(preRestoreState)) { @@ -869,13 +709,7 @@ abstract class SWB { } await status; } - for (int i = 0; i < epicCashWallets.length; i++) { - // check if cancel was requested and restore previous state - if (_checkShouldCancel(preRestoreState)) { - return false; - } - await asyncRestore(epicCashWallets[i], uiState, walletsService); - } + if (!Platform.isLinux) await Wakelock.disable(); // check if cancel was requested and restore previous state if (_checkShouldCancel(preRestoreState)) { diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart index 5ad8cc4f3..5e5142425 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart @@ -445,7 +445,7 @@ class _StackRestoreProgressViewState }, style: Theme.of(context) .extension()! - .getSecondaryEnabledButtonColor(context), + .getPrimaryEnabledButtonColor(context), child: Text( _success ? "OK" : "Cancel restore process", style: STextStyles.button(context).copyWith( diff --git a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart index 30f04e918..3a5cebdec 100644 --- a/lib/services/coins/bitcoincash/bitcoincash_wallet.dart +++ b/lib/services/coins/bitcoincash/bitcoincash_wallet.dart @@ -43,7 +43,7 @@ import 'package:stackwallet/utilities/prefs.dart'; import 'package:tuple/tuple.dart'; import 'package:uuid/uuid.dart'; -const int MINIMUM_CONFIRMATIONS = 3; +const int MINIMUM_CONFIRMATIONS = 1; const int DUST_LIMIT = 546; const String GENESIS_HASH_MAINNET = @@ -265,6 +265,11 @@ class BitcoinCashWallet extends CoinServiceAPI { DerivePathType addressType({required String address}) { Uint8List? decodeBase58; Segwit? decodeBech32; + try { + if (Bitbox.Address.detectFormat(address) == 0) { + address = Bitbox.Address.toLegacyAddress(address); + } + } catch (e, s) {} try { decodeBase58 = bs58check.decode(address); } catch (err) { @@ -825,9 +830,6 @@ class BitcoinCashWallet extends CoinServiceAPI { /// Refreshes display data for the wallet @override Future refresh() async { - final bchaddr = Bitbox.Address.toCashAddress(await currentReceivingAddress); - print("bchaddr: $bchaddr ${await currentReceivingAddress}"); - if (refreshMutex) { Logging.instance.log("$walletId $walletName refreshMutex denied", level: LogLevel.Info); @@ -1384,7 +1386,9 @@ class BitcoinCashWallet extends CoinServiceAPI { initialChangeAddressP2SH, 1, DerivePathType.bip49); // this._currentReceivingAddress = Future(() => initialReceivingAddress); - _currentReceivingAddressP2PKH = Future(() => initialReceivingAddressP2PKH); + + var newaddr = await _getCurrentAddressForChain(0, DerivePathType.bip44); + _currentReceivingAddressP2PKH = Future(() => newaddr); _currentReceivingAddressP2SH = Future(() => initialReceivingAddressP2SH); Logging.instance.log("_generateNewWalletFinished", level: LogLevel.Info); @@ -1521,6 +1525,11 @@ class BitcoinCashWallet extends CoinServiceAPI { print("Array key is ${jsonEncode(arrayKey)}"); final internalChainArray = DB.instance.get(boxName: walletId, key: arrayKey); + if (derivePathType == DerivePathType.bip44) { + if (Bitbox.Address.detectFormat(internalChainArray.last as String) == 1) { + return Bitbox.Address.toCashAddress(internalChainArray.last as String); + } + } return internalChainArray.last as String; } @@ -1986,6 +1995,9 @@ class BitcoinCashWallet extends CoinServiceAPI { /// Returns the scripthash or throws an exception on invalid bch address String _convertToScriptHash(String bchAddress, NetworkType network) { try { + if (Bitbox.Address.detectFormat(bchAddress) == 0) { + bchAddress = Bitbox.Address.toLegacyAddress(bchAddress); + } final output = Address.addressToOutputScript(bchAddress, network); final hash = sha256.convert(output.toList(growable: false)).toString(); @@ -2058,11 +2070,27 @@ class BitcoinCashWallet extends CoinServiceAPI { } Future _fetchTransactionData() async { - final List allAddresses = await _fetchAllOwnAddresses(); + List allAddressesOld = await _fetchAllOwnAddresses(); + List allAddresses = []; + for (String address in allAddressesOld) { + if (Bitbox.Address.detectFormat(address) == 1) { + allAddresses.add(Bitbox.Address.toCashAddress(address)); + } else { + allAddresses.add(address); + } + } - final changeAddressesP2PKH = + var changeAddressesP2PKHOld = DB.instance.get(boxName: walletId, key: 'changeAddressesP2PKH') as List; + List changeAddressesP2PKH = []; + for (var address in changeAddressesP2PKHOld) { + if (Bitbox.Address.detectFormat(address as String) == 1) { + changeAddressesP2PKH.add(Bitbox.Address.toCashAddress(address)); + } else { + changeAddressesP2PKH.add(address); + } + } final List> allTxHashes = await _fetchHistory(allAddresses); @@ -2087,7 +2115,16 @@ class BitcoinCashWallet extends CoinServiceAPI { if (txHeight > 0 && txHeight < latestTxnBlockHeight - MINIMUM_CONFIRMATIONS) { if (unconfirmedCachedTransactions[tx["tx_hash"] as String] == null) { - allTxHashes.remove(tx); + print(cachedTransactions.findTransaction(tx["tx_hash"] as String)); + print(unconfirmedCachedTransactions[tx["tx_hash"] as String]); + final cachedTx = + cachedTransactions.findTransaction(tx["tx_hash"] as String); + if (!(cachedTx != null && + addressType(address: cachedTx.address) == + DerivePathType.bip44 && + Bitbox.Address.detectFormat(cachedTx.address) == 1)) { + allTxHashes.remove(tx); + } } } } @@ -2096,7 +2133,6 @@ class BitcoinCashWallet extends CoinServiceAPI { List> allTransactions = []; for (final txHash in allTxHashes) { - Logging.instance.log("bch: $txHash", level: LogLevel.Info); final tx = await cachedElectrumXClient.getTransaction( txHash: txHash["tx_hash"] as String, verbose: true, @@ -2166,7 +2202,8 @@ class BitcoinCashWallet extends CoinServiceAPI { .log("recipientsArray: $recipientsArray", level: LogLevel.Info); final foundInSenders = - allAddresses.any((element) => sendersArray.contains(element)); + allAddresses.any((element) => sendersArray.contains(element)) || + allAddressesOld.any((element) => sendersArray.contains(element)); Logging.instance .log("foundInSenders: $foundInSenders", level: LogLevel.Info); @@ -2228,7 +2265,8 @@ class BitcoinCashWallet extends CoinServiceAPI { .toBigInt() .toInt(); totalOut += value; - if (allAddresses.contains(address)) { + if (allAddresses.contains(address) || + allAddressesOld.contains(address)) { outputAmtAddressedToWallet += value; } } @@ -2743,7 +2781,10 @@ class BitcoinCashWallet extends CoinServiceAPI { for (final output in tx["vout"] as List) { final n = output["n"]; if (n != null && n == utxosToUse[i].vout) { - final address = output["scriptPubKey"]["addresses"][0] as String; + String address = output["scriptPubKey"]["addresses"][0] as String; + if (Bitbox.Address.detectFormat(address) == 0) { + address = Bitbox.Address.toLegacyAddress(address); + } if (!addressTxid.containsKey(address)) { addressTxid[address] = []; } @@ -2772,8 +2813,13 @@ class BitcoinCashWallet extends CoinServiceAPI { derivePathType: DerivePathType.bip44, ); for (int i = 0; i < p2pkhLength; i++) { + String address = addressesP2PKH[i]; + if (Bitbox.Address.detectFormat(address) == 0) { + address = Bitbox.Address.toLegacyAddress(address); + } + // receives - final receiveDerivation = receiveDerivations[addressesP2PKH[i]]; + final receiveDerivation = receiveDerivations[address]; // if a match exists it will not be null if (receiveDerivation != null) { final data = P2PKH( @@ -2783,7 +2829,7 @@ class BitcoinCashWallet extends CoinServiceAPI { network: _network, ).data; - for (String tx in addressTxid[addressesP2PKH[i]]!) { + for (String tx in addressTxid[address]!) { results[tx] = { "output": data.output, "keyPair": ECPair.fromWIF( @@ -2794,7 +2840,7 @@ class BitcoinCashWallet extends CoinServiceAPI { } } else { // if its not a receive, check change - final changeDerivation = changeDerivations[addressesP2PKH[i]]; + final changeDerivation = changeDerivations[address]; // if a match exists it will not be null if (changeDerivation != null) { final data = P2PKH( @@ -2804,7 +2850,7 @@ class BitcoinCashWallet extends CoinServiceAPI { network: _network, ).data; - for (String tx in addressTxid[addressesP2PKH[i]]!) { + for (String tx in addressTxid[address]!) { results[tx] = { "output": data.output, "keyPair": ECPair.fromWIF( @@ -3377,8 +3423,9 @@ class BitcoinCashWallet extends CoinServiceAPI { 0, DerivePathType .bip44); // Add that new receiving address to the array of receiving addresses - _currentReceivingAddressP2PKH = Future(() => - newReceivingAddress); // Set the new receiving address that the service + var newaddr = await _getCurrentAddressForChain(0, DerivePathType.bip44); + _currentReceivingAddressP2PKH = Future( + () => newaddr); // Set the new receiving address that the service return true; } catch (e, s) { diff --git a/lib/services/coins/epiccash/epiccash_wallet.dart b/lib/services/coins/epiccash/epiccash_wallet.dart index 112e6c176..7ccb7feaf 100644 --- a/lib/services/coins/epiccash/epiccash_wallet.dart +++ b/lib/services/coins/epiccash/epiccash_wallet.dart @@ -1990,7 +1990,6 @@ class EpicCashWallet extends CoinServiceAPI { Future refreshIfThereIsNewData() async { if (_hasCalledExit) return false; - Logging.instance.log("Can we do this here?", level: LogLevel.Fatal); // TODO returning true here signals this class to call refresh() after which it will fire an event that notifies the UI that new data has been fetched/found for this wallet return true; // TODO: do a quick check to see if there is any new data that would require a refresh diff --git a/lib/services/node_service.dart b/lib/services/node_service.dart index 06c8c40ea..5b9fe5063 100644 --- a/lib/services/node_service.dart +++ b/lib/services/node_service.dart @@ -39,6 +39,19 @@ class NodeService extends ChangeNotifier { key: savedNode.id, value: defaultNode.copyWith(enabled: savedNode.enabled)); } + + // check if a default node is the primary node for the crypto currency + // and update it if needed + final coin = coinFromPrettyName(defaultNode.coinName); + final primaryNode = getPrimaryNodeFor(coin: coin); + if (primaryNode != null && primaryNode.id == defaultNode.id) { + await setPrimaryNodeFor( + coin: coin, + node: defaultNode.copyWith( + enabled: primaryNode.enabled, + ), + ); + } } } diff --git a/lib/services/price.dart b/lib/services/price.dart index 4f6650b06..b44e055d5 100644 --- a/lib/services/price.dart +++ b/lib/services/price.dart @@ -78,12 +78,12 @@ class PriceAPI { } final externalCalls = Prefs.instance.externalCalls; - if (!Logger.isTestEnv && !externalCalls) { + if ((!Logger.isTestEnv && !externalCalls) || + !(await Prefs.instance.isExternalCallsSet())) { Logging.instance.log("User does not want to use external calls", level: LogLevel.Info); return _cachedPrices; } - Map> result = {}; try { final uri = Uri.parse( @@ -123,7 +123,8 @@ class PriceAPI { static Future?> availableBaseCurrencies() async { final externalCalls = Prefs.instance.externalCalls; - if (!Logger.isTestEnv && !externalCalls) { + if ((!Logger.isTestEnv && !externalCalls) || + !(await Prefs.instance.isExternalCallsSet())) { Logging.instance.log("User does not want to use external calls", level: LogLevel.Info); return null; diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 4eb69d7e1..c4ac4bbe9 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/util.dart'; class _LayoutSizing { const _LayoutSizing(); @@ -14,7 +15,12 @@ class _LayoutSizing { abstract class Constants { static const size = _LayoutSizing(); - static final bool enableExchange = !Platform.isIOS; + static void exchangeForExperiencedUsers(int count) { + enableExchange = + Util.isDesktop || Platform.isAndroid || count > 5 || !Platform.isIOS; + } + + static bool enableExchange = Util.isDesktop || !Platform.isIOS; //TODO: correct for monero? static const int satsPerCoinMonero = 1000000000000; @@ -36,7 +42,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentHiveDbVersion = 2; + static const int currentHiveDbVersion = 3; static List possibleLengthsForCoin(Coin coin) { final List values = []; diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 26af1c48a..d1763e266 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -143,6 +143,18 @@ class DbVersionMigrator { // try to continue migrating return await migrate(2); + case 2: + await Hive.openBox(DB.boxNamePrefs); + final prefs = Prefs.instance; + await prefs.init(); + if (!(await prefs.isExternalCallsSet())) { + prefs.externalCalls = true; + } + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 3); + return await migrate(3); default: // finally return diff --git a/lib/utilities/default_nodes.dart b/lib/utilities/default_nodes.dart index b09787d33..566b829ce 100644 --- a/lib/utilities/default_nodes.dart +++ b/lib/utilities/default_nodes.dart @@ -70,28 +70,24 @@ abstract class DefaultNodes { isDown: false, ); - // TODO: eventually enable ssl and set scheme to https - // currently get certificate failure static NodeModel get monero => NodeModel( - host: "http://monero.stackwallet.com", + host: "https://monero.stackwallet.com", port: 18081, name: defaultName, id: _nodeId(Coin.monero), - useSSL: false, + useSSL: true, enabled: true, coinName: Coin.monero.name, isFailover: true, isDown: false, ); - // TODO: eventually enable ssl and set scheme to https - // currently get certificate failure static NodeModel get wownero => NodeModel( - host: "http://eu-west-2.wow.xmr.pm", + host: "https://wownero.stackwallet.com", port: 34568, name: defaultName, id: _nodeId(Coin.wownero), - useSSL: false, + useSSL: true, enabled: true, coinName: Coin.wownero.name, isFailover: true, diff --git a/lib/utilities/enums/coin_enum.dart b/lib/utilities/enums/coin_enum.dart index 78a04e220..c25a5ca4e 100644 --- a/lib/utilities/enums/coin_enum.dart +++ b/lib/utilities/enums/coin_enum.dart @@ -181,25 +181,32 @@ Coin coinFromPrettyName(String name) { case "Bitcoin": case "bitcoin": return Coin.bitcoin; + case "Bitcoincash": case "bitcoincash": case "Bitcoin Cash": return Coin.bitcoincash; + case "Dogecoin": case "dogecoin": return Coin.dogecoin; + case "Epic Cash": case "epicCash": return Coin.epicCash; + case "Firo": case "firo": return Coin.firo; + case "Monero": case "monero": return Coin.monero; + case "Namecoin": case "namecoin": return Coin.namecoin; + case "Bitcoin Testnet": case "tBitcoin": case "bitcoinTestNet": @@ -208,19 +215,24 @@ Coin coinFromPrettyName(String name) { case "Bitcoincash Testnet": case "tBitcoin Cash": case "Bitcoin Cash Testnet": + case "bitcoincashTestnet": return Coin.bitcoincashTestnet; + case "Firo Testnet": case "tFiro": case "firoTestNet": return Coin.firoTestNet; + case "Dogecoin Testnet": case "tDogecoin": case "dogecoinTestNet": return Coin.dogecoinTestNet; + case "Wownero": case "tWownero": case "wownero": return Coin.wownero; + default: throw ArgumentError.value( name, "name", "No Coin enum value with that prettyName"); diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 2fc21a78d..246291053 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -571,4 +571,13 @@ class Prefs extends ChangeNotifier { boxName: DB.boxNamePrefs, key: "externalCalls") as bool? ?? true; } + + Future isExternalCallsSet() async { + if (await DB.instance + .get(boxName: DB.boxNamePrefs, key: "externalCalls") == + null) { + return false; + } + return true; + } } diff --git a/lib/widgets/node_options_sheet.dart b/lib/widgets/node_options_sheet.dart index 58a5bf30a..effe97097 100644 --- a/lib/widgets/node_options_sheet.dart +++ b/lib/widgets/node_options_sheet.dart @@ -306,10 +306,10 @@ class NodeOptionsSheet extends ConsumerWidget { style: status == "Connected" ? Theme.of(context) .extension()! - .getPrimaryEnabledButtonColor(context) + .getPrimaryDisabledButtonColor(context) : Theme.of(context) .extension()! - .getPrimaryDisabledButtonColor(context), + .getPrimaryEnabledButtonColor(context), onPressed: status == "Connected" ? null : () async { diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 198975556..59063791f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,7 @@ import FlutterMacOS import Foundation import connectivity_plus_macos +import device_info_plus import devicelocale import flutter_libepiccash import flutter_local_notifications @@ -22,6 +23,7 @@ import window_size func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) FlutterLibepiccashPlugin.register(with: registry.registrar(forPlugin: "FlutterLibepiccashPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 1a564066d..394d1ec8e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -380,6 +380,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.2.2" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "7.0.1" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" devicelocale: dependency: "direct main" description: @@ -1396,7 +1410,14 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" + string_validator: + dependency: "direct main" + description: + name: string_validator + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" sync_http: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index aabf3f3de..42934fd5c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: Stack Wallet # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.5.8+78 +version: 1.5.9+79 environment: sdk: ">=2.17.0 <3.0.0" @@ -115,6 +115,7 @@ dependencies: wakelock: ^0.6.2 intl: ^0.17.0 devicelocale: ^0.5.0 + device_info_plus: ^7.0.1 keyboard_dismisser: ^3.0.0 another_flushbar: ^1.10.28 tuple: ^2.0.0 @@ -132,6 +133,7 @@ dependencies: isar: 3.0.0-dev.10 isar_flutter_libs: 3.0.0-dev.10 # contains the binaries dropdown_button2: 1.7.2 + string_validator: ^0.3.0 dev_dependencies: flutter_test: diff --git a/test/notifications/notification_card_test.dart b/test/notifications/notification_card_test.dart new file mode 100644 index 000000000..3ac11c21c --- /dev/null +++ b/test/notifications/notification_card_test.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/models/notification_model.dart'; +import 'package:stackwallet/notifications/notification_card.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +void main() { + testWidgets("test notification card", (widgetTester) async { + final key = UniqueKey(); + final notificationCard = NotificationCard( + key: key, + notification: NotificationModel( + id: 1, + title: "notification title", + description: "notification description", + iconAssetName: Assets.svg.iconFor(coin: Coin.bitcoin), + date: DateTime.parse("1662544771"), + walletId: "wallet id", + read: true, + shouldWatchForUpdates: true, + coinName: "Bitcoin"), + ); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: notificationCard, + ), + ), + ); + + expect(find.byWidget(notificationCard), findsOneWidget); + expect(find.text("notification title"), findsOneWidget); + expect(find.text("notification description"), findsOneWidget); + expect(find.byType(SvgPicture), findsOneWidget); + }); +} diff --git a/test/pages/send_view/send_view_test.dart b/test/pages/send_view/send_view_test.dart new file mode 100644 index 000000000..73e5b8623 --- /dev/null +++ b/test/pages/send_view/send_view_test.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:stackwallet/models/send_view_auto_fill_data.dart'; +import 'package:stackwallet/pages/send_view/send_view.dart'; +import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart'; +import 'package:stackwallet/services/coins/coin_service.dart'; +import 'package:stackwallet/services/coins/manager.dart'; +import 'package:stackwallet/services/locale_service.dart'; +import 'package:stackwallet/services/node_service.dart'; +import 'package:stackwallet/services/wallets.dart'; +import 'package:stackwallet/services/wallets_service.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/prefs.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'send_view_test.mocks.dart'; + +@GenerateMocks([ + Wallets, + WalletsService, + NodeService, + BitcoinWallet, + LocaleService, + Prefs, +], customMocks: [ + MockSpec(returnNullOnMissingStub: true), + MockSpec(returnNullOnMissingStub: true), +]) +void main() { + testWidgets("Send to valid address", (widgetTester) async { + final mockWallets = MockWallets(); + final mockWalletsService = MockWalletsService(); + final mockNodeService = MockNodeService(); + final CoinServiceAPI wallet = MockBitcoinWallet(); + final mockLocaleService = MockLocaleService(); + final mockPrefs = MockPrefs(); + + when(wallet.coin).thenAnswer((_) => Coin.bitcoin); + when(wallet.walletName).thenAnswer((_) => "some wallet"); + when(wallet.walletId).thenAnswer((_) => "wallet id"); + + final manager = Manager(wallet); + when(mockWallets.getManagerProvider("wallet id")).thenAnswer( + (realInvocation) => ChangeNotifierProvider((ref) => manager)); + when(mockWallets.getManager("wallet id")) + .thenAnswer((realInvocation) => manager); + + when(mockLocaleService.locale).thenAnswer((_) => "en_US"); + when(mockPrefs.currency).thenAnswer((_) => "USD"); + when(wallet.validateAddress("send to address")) + .thenAnswer((realInvocation) => true); + + await widgetTester.pumpWidget( + ProviderScope( + overrides: [ + walletsChangeNotifierProvider.overrideWithValue(mockWallets), + walletsServiceChangeNotifierProvider + .overrideWithValue(mockWalletsService), + nodeServiceChangeNotifierProvider.overrideWithValue(mockNodeService), + localeServiceChangeNotifierProvider + .overrideWithValue(mockLocaleService), + prefsChangeNotifierProvider.overrideWithValue(mockPrefs), + // previewTxButtonStateProvider + ], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: SendView( + walletId: "wallet id", + coin: Coin.bitcoin, + autoFillData: SendViewAutoFillData( + address: "send to address", contactLabel: "contact label"), + ), + ), + ), + ); + + expect(find.text("Send to"), findsOneWidget); + expect(find.text("Amount"), findsOneWidget); + expect(find.text("Note (optional)"), findsOneWidget); + expect(find.text("Transaction fee (estimated)"), findsOneWidget); + verify(manager.validateAddress("send to address")).called(1); + }); + + testWidgets("Send to invalid address", (widgetTester) async { + final mockWallets = MockWallets(); + final mockWalletsService = MockWalletsService(); + final mockNodeService = MockNodeService(); + final CoinServiceAPI wallet = MockBitcoinWallet(); + final mockLocaleService = MockLocaleService(); + final mockPrefs = MockPrefs(); + + when(wallet.coin).thenAnswer((_) => Coin.bitcoin); + when(wallet.walletName).thenAnswer((_) => "some wallet"); + when(wallet.walletId).thenAnswer((_) => "wallet id"); + + final manager = Manager(wallet); + when(mockWallets.getManagerProvider("wallet id")).thenAnswer( + (realInvocation) => ChangeNotifierProvider((ref) => manager)); + when(mockWallets.getManager("wallet id")) + .thenAnswer((realInvocation) => manager); + + when(mockLocaleService.locale).thenAnswer((_) => "en_US"); + when(mockPrefs.currency).thenAnswer((_) => "USD"); + when(wallet.validateAddress("send to address")) + .thenAnswer((realInvocation) => false); + + // when(manager.isOwnAddress("send to address")) + // .thenAnswer((realInvocation) => Future(() => true)); + + await widgetTester.pumpWidget( + ProviderScope( + overrides: [ + walletsChangeNotifierProvider.overrideWithValue(mockWallets), + walletsServiceChangeNotifierProvider + .overrideWithValue(mockWalletsService), + nodeServiceChangeNotifierProvider.overrideWithValue(mockNodeService), + localeServiceChangeNotifierProvider + .overrideWithValue(mockLocaleService), + prefsChangeNotifierProvider.overrideWithValue(mockPrefs), + // previewTxButtonStateProvider + ], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: SendView( + walletId: "wallet id", + coin: Coin.bitcoin, + autoFillData: SendViewAutoFillData( + address: "send to address", contactLabel: "contact label"), + ), + ), + ), + ); + + expect(find.text("Send to"), findsOneWidget); + expect(find.text("Amount"), findsOneWidget); + expect(find.text("Note (optional)"), findsOneWidget); + expect(find.text("Transaction fee (estimated)"), findsOneWidget); + expect(find.text("Invalid address"), findsOneWidget); + verify(manager.validateAddress("send to address")).called(1); + }); +} diff --git a/test/pages/send_view/send_view_test.mocks.dart b/test/pages/send_view/send_view_test.mocks.dart new file mode 100644 index 000000000..a07377309 --- /dev/null +++ b/test/pages/send_view/send_view_test.mocks.dart @@ -0,0 +1,2626 @@ +// Mocks generated by Mockito 5.3.2 from annotations +// in stackwallet/test/pages/send_view_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i16; +import 'dart:ui' as _i18; + +import 'package:decimal/decimal.dart' as _i10; +import 'package:flutter/foundation.dart' as _i4; +import 'package:flutter_riverpod/flutter_riverpod.dart' as _i5; +import 'package:mockito/mockito.dart' as _i1; +import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i12; +import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i11; +import 'package:stackwallet/models/models.dart' as _i9; +import 'package:stackwallet/models/node_model.dart' as _i19; +import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart' + as _i23; +import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i20; +import 'package:stackwallet/services/coins/coin_service.dart' as _i13; +import 'package:stackwallet/services/coins/manager.dart' as _i6; +import 'package:stackwallet/services/locale_service.dart' as _i21; +import 'package:stackwallet/services/node_service.dart' as _i3; +import 'package:stackwallet/services/transaction_notification_tracker.dart' + as _i8; +import 'package:stackwallet/services/wallets.dart' as _i14; +import 'package:stackwallet/services/wallets_service.dart' as _i2; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i24; +import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i15; +import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i22; +import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart' + as _i7; +import 'package:stackwallet/utilities/prefs.dart' as _i17; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeWalletsService_0 extends _i1.SmartFake + implements _i2.WalletsService { + _FakeWalletsService_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeNodeService_1 extends _i1.SmartFake implements _i3.NodeService { + _FakeNodeService_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeChangeNotifierProvider_2 + extends _i1.SmartFake implements _i5.ChangeNotifierProvider { + _FakeChangeNotifierProvider_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeManager_3 extends _i1.SmartFake implements _i6.Manager { + _FakeManager_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFlutterSecureStorageInterface_4 extends _i1.SmartFake + implements _i7.FlutterSecureStorageInterface { + _FakeFlutterSecureStorageInterface_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeTransactionNotificationTracker_5 extends _i1.SmartFake + implements _i8.TransactionNotificationTracker { + _FakeTransactionNotificationTracker_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeUtxoData_6 extends _i1.SmartFake implements _i9.UtxoData { + _FakeUtxoData_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDecimal_7 extends _i1.SmartFake implements _i10.Decimal { + _FakeDecimal_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFeeObject_8 extends _i1.SmartFake implements _i9.FeeObject { + _FakeFeeObject_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeTransactionData_9 extends _i1.SmartFake + implements _i9.TransactionData { + _FakeTransactionData_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumX_10 extends _i1.SmartFake implements _i11.ElectrumX { + _FakeElectrumX_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCachedElectrumX_11 extends _i1.SmartFake + implements _i12.CachedElectrumX { + _FakeCachedElectrumX_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeElectrumXNode_12 extends _i1.SmartFake + implements _i11.ElectrumXNode { + _FakeElectrumXNode_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCoinServiceAPI_13 extends _i1.SmartFake + implements _i13.CoinServiceAPI { + _FakeCoinServiceAPI_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Wallets]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWallets extends _i1.Mock implements _i14.Wallets { + MockWallets() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WalletsService get walletsService => (super.noSuchMethod( + Invocation.getter(#walletsService), + returnValue: _FakeWalletsService_0( + this, + Invocation.getter(#walletsService), + ), + ) as _i2.WalletsService); + @override + set walletsService(_i2.WalletsService? _walletsService) => super.noSuchMethod( + Invocation.setter( + #walletsService, + _walletsService, + ), + returnValueForMissingStub: null, + ); + @override + _i3.NodeService get nodeService => (super.noSuchMethod( + Invocation.getter(#nodeService), + returnValue: _FakeNodeService_1( + this, + Invocation.getter(#nodeService), + ), + ) as _i3.NodeService); + @override + set nodeService(_i3.NodeService? _nodeService) => super.noSuchMethod( + Invocation.setter( + #nodeService, + _nodeService, + ), + returnValueForMissingStub: null, + ); + @override + bool get hasWallets => (super.noSuchMethod( + Invocation.getter(#hasWallets), + returnValue: false, + ) as bool); + @override + List<_i5.ChangeNotifierProvider<_i6.Manager>> get managerProviders => + (super.noSuchMethod( + Invocation.getter(#managerProviders), + returnValue: <_i5.ChangeNotifierProvider<_i6.Manager>>[], + ) as List<_i5.ChangeNotifierProvider<_i6.Manager>>); + @override + List<_i6.Manager> get managers => (super.noSuchMethod( + Invocation.getter(#managers), + returnValue: <_i6.Manager>[], + ) as List<_i6.Manager>); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + List getWalletIdsFor({required _i15.Coin? coin}) => + (super.noSuchMethod( + Invocation.method( + #getWalletIdsFor, + [], + {#coin: coin}, + ), + returnValue: [], + ) as List); + @override + Map<_i15.Coin, List<_i5.ChangeNotifierProvider<_i6.Manager>>> + getManagerProvidersByCoin() => (super.noSuchMethod( + Invocation.method( + #getManagerProvidersByCoin, + [], + ), + returnValue: <_i15.Coin, + List<_i5.ChangeNotifierProvider<_i6.Manager>>>{}, + ) as Map<_i15.Coin, List<_i5.ChangeNotifierProvider<_i6.Manager>>>); + @override + _i5.ChangeNotifierProvider<_i6.Manager> getManagerProvider( + String? walletId) => + (super.noSuchMethod( + Invocation.method( + #getManagerProvider, + [walletId], + ), + returnValue: _FakeChangeNotifierProvider_2<_i6.Manager>( + this, + Invocation.method( + #getManagerProvider, + [walletId], + ), + ), + ) as _i5.ChangeNotifierProvider<_i6.Manager>); + @override + _i6.Manager getManager(String? walletId) => (super.noSuchMethod( + Invocation.method( + #getManager, + [walletId], + ), + returnValue: _FakeManager_3( + this, + Invocation.method( + #getManager, + [walletId], + ), + ), + ) as _i6.Manager); + @override + void addWallet({ + required String? walletId, + required _i6.Manager? manager, + }) => + super.noSuchMethod( + Invocation.method( + #addWallet, + [], + { + #walletId: walletId, + #manager: manager, + }, + ), + returnValueForMissingStub: null, + ); + @override + void removeWallet({required String? walletId}) => super.noSuchMethod( + Invocation.method( + #removeWallet, + [], + {#walletId: walletId}, + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future load(_i17.Prefs? prefs) => (super.noSuchMethod( + Invocation.method( + #load, + [prefs], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future loadAfterStackRestore( + _i17.Prefs? prefs, + List<_i6.Manager>? managers, + ) => + (super.noSuchMethod( + Invocation.method( + #loadAfterStackRestore, + [ + prefs, + managers, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [WalletsService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWalletsService extends _i1.Mock implements _i2.WalletsService { + MockWalletsService() { + _i1.throwOnMissingStub(this); + } + + @override + _i16.Future> get walletNames => + (super.noSuchMethod( + Invocation.getter(#walletNames), + returnValue: _i16.Future>.value( + {}), + ) as _i16.Future>); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + _i16.Future renameWallet({ + required String? from, + required String? to, + required bool? shouldNotifyListeners, + }) => + (super.noSuchMethod( + Invocation.method( + #renameWallet, + [], + { + #from: from, + #to: to, + #shouldNotifyListeners: shouldNotifyListeners, + }, + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future addExistingStackWallet({ + required String? name, + required String? walletId, + required _i15.Coin? coin, + required bool? shouldNotifyListeners, + }) => + (super.noSuchMethod( + Invocation.method( + #addExistingStackWallet, + [], + { + #name: name, + #walletId: walletId, + #coin: coin, + #shouldNotifyListeners: shouldNotifyListeners, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future addNewWallet({ + required String? name, + required _i15.Coin? coin, + required bool? shouldNotifyListeners, + }) => + (super.noSuchMethod( + Invocation.method( + #addNewWallet, + [], + { + #name: name, + #coin: coin, + #shouldNotifyListeners: shouldNotifyListeners, + }, + ), + returnValue: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future> getFavoriteWalletIds() => (super.noSuchMethod( + Invocation.method( + #getFavoriteWalletIds, + [], + ), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + _i16.Future saveFavoriteWalletIds(List? walletIds) => + (super.noSuchMethod( + Invocation.method( + #saveFavoriteWalletIds, + [walletIds], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future addFavorite(String? walletId) => (super.noSuchMethod( + Invocation.method( + #addFavorite, + [walletId], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future removeFavorite(String? walletId) => (super.noSuchMethod( + Invocation.method( + #removeFavorite, + [walletId], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future moveFavorite({ + required int? fromIndex, + required int? toIndex, + }) => + (super.noSuchMethod( + Invocation.method( + #moveFavorite, + [], + { + #fromIndex: fromIndex, + #toIndex: toIndex, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future checkForDuplicate(String? name) => (super.noSuchMethod( + Invocation.method( + #checkForDuplicate, + [name], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future getWalletId(String? walletName) => (super.noSuchMethod( + Invocation.method( + #getWalletId, + [walletName], + ), + returnValue: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future isMnemonicVerified({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #isMnemonicVerified, + [], + {#walletId: walletId}, + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future setMnemonicVerified({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #setMnemonicVerified, + [], + {#walletId: walletId}, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future deleteWallet( + String? name, + bool? shouldNotifyListeners, + ) => + (super.noSuchMethod( + Invocation.method( + #deleteWallet, + [ + name, + shouldNotifyListeners, + ], + ), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future refreshWallets(bool? shouldNotifyListeners) => + (super.noSuchMethod( + Invocation.method( + #refreshWallets, + [shouldNotifyListeners], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [NodeService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockNodeService extends _i1.Mock implements _i3.NodeService { + MockNodeService() { + _i1.throwOnMissingStub(this); + } + + @override + _i7.FlutterSecureStorageInterface get secureStorageInterface => + (super.noSuchMethod( + Invocation.getter(#secureStorageInterface), + returnValue: _FakeFlutterSecureStorageInterface_4( + this, + Invocation.getter(#secureStorageInterface), + ), + ) as _i7.FlutterSecureStorageInterface); + @override + List<_i19.NodeModel> get primaryNodes => (super.noSuchMethod( + Invocation.getter(#primaryNodes), + returnValue: <_i19.NodeModel>[], + ) as List<_i19.NodeModel>); + @override + List<_i19.NodeModel> get nodes => (super.noSuchMethod( + Invocation.getter(#nodes), + returnValue: <_i19.NodeModel>[], + ) as List<_i19.NodeModel>); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + _i16.Future updateDefaults() => (super.noSuchMethod( + Invocation.method( + #updateDefaults, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future setPrimaryNodeFor({ + required _i15.Coin? coin, + required _i19.NodeModel? node, + bool? shouldNotifyListeners = false, + }) => + (super.noSuchMethod( + Invocation.method( + #setPrimaryNodeFor, + [], + { + #coin: coin, + #node: node, + #shouldNotifyListeners: shouldNotifyListeners, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i19.NodeModel? getPrimaryNodeFor({required _i15.Coin? coin}) => + (super.noSuchMethod(Invocation.method( + #getPrimaryNodeFor, + [], + {#coin: coin}, + )) as _i19.NodeModel?); + @override + List<_i19.NodeModel> getNodesFor(_i15.Coin? coin) => (super.noSuchMethod( + Invocation.method( + #getNodesFor, + [coin], + ), + returnValue: <_i19.NodeModel>[], + ) as List<_i19.NodeModel>); + @override + _i19.NodeModel? getNodeById({required String? id}) => + (super.noSuchMethod(Invocation.method( + #getNodeById, + [], + {#id: id}, + )) as _i19.NodeModel?); + @override + List<_i19.NodeModel> failoverNodesFor({required _i15.Coin? coin}) => + (super.noSuchMethod( + Invocation.method( + #failoverNodesFor, + [], + {#coin: coin}, + ), + returnValue: <_i19.NodeModel>[], + ) as List<_i19.NodeModel>); + @override + _i16.Future add( + _i19.NodeModel? node, + String? password, + bool? shouldNotifyListeners, + ) => + (super.noSuchMethod( + Invocation.method( + #add, + [ + node, + password, + shouldNotifyListeners, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future delete( + String? id, + bool? shouldNotifyListeners, + ) => + (super.noSuchMethod( + Invocation.method( + #delete, + [ + id, + shouldNotifyListeners, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future setEnabledState( + String? id, + bool? enabled, + bool? shouldNotifyListeners, + ) => + (super.noSuchMethod( + Invocation.method( + #setEnabledState, + [ + id, + enabled, + shouldNotifyListeners, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future edit( + _i19.NodeModel? editedNode, + String? password, + bool? shouldNotifyListeners, + ) => + (super.noSuchMethod( + Invocation.method( + #edit, + [ + editedNode, + password, + shouldNotifyListeners, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future updateCommunityNodes() => (super.noSuchMethod( + Invocation.method( + #updateCommunityNodes, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [BitcoinWallet]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBitcoinWallet extends _i1.Mock implements _i20.BitcoinWallet { + MockBitcoinWallet() { + _i1.throwOnMissingStub(this); + } + + @override + set timer(_i16.Timer? _timer) => super.noSuchMethod( + Invocation.setter( + #timer, + _timer, + ), + returnValueForMissingStub: null, + ); + @override + _i8.TransactionNotificationTracker get txTracker => (super.noSuchMethod( + Invocation.getter(#txTracker), + returnValue: _FakeTransactionNotificationTracker_5( + this, + Invocation.getter(#txTracker), + ), + ) as _i8.TransactionNotificationTracker); + @override + set txTracker(_i8.TransactionNotificationTracker? _txTracker) => + super.noSuchMethod( + Invocation.setter( + #txTracker, + _txTracker, + ), + returnValueForMissingStub: null, + ); + @override + List<_i9.UtxoObject> get outputsList => (super.noSuchMethod( + Invocation.getter(#outputsList), + returnValue: <_i9.UtxoObject>[], + ) as List<_i9.UtxoObject>); + @override + set outputsList(List<_i9.UtxoObject>? _outputsList) => super.noSuchMethod( + Invocation.setter( + #outputsList, + _outputsList, + ), + returnValueForMissingStub: null, + ); + @override + bool get longMutex => (super.noSuchMethod( + Invocation.getter(#longMutex), + returnValue: false, + ) as bool); + @override + set longMutex(bool? _longMutex) => super.noSuchMethod( + Invocation.setter( + #longMutex, + _longMutex, + ), + returnValueForMissingStub: null, + ); + @override + bool get refreshMutex => (super.noSuchMethod( + Invocation.getter(#refreshMutex), + returnValue: false, + ) as bool); + @override + set refreshMutex(bool? _refreshMutex) => super.noSuchMethod( + Invocation.setter( + #refreshMutex, + _refreshMutex, + ), + returnValueForMissingStub: null, + ); + @override + bool get isActive => (super.noSuchMethod( + Invocation.getter(#isActive), + returnValue: false, + ) as bool); + @override + set isActive(bool? _isActive) => super.noSuchMethod( + Invocation.setter( + #isActive, + _isActive, + ), + returnValueForMissingStub: null, + ); + @override + set isFavorite(bool? markFavorite) => super.noSuchMethod( + Invocation.setter( + #isFavorite, + markFavorite, + ), + returnValueForMissingStub: null, + ); + @override + bool get isFavorite => (super.noSuchMethod( + Invocation.getter(#isFavorite), + returnValue: false, + ) as bool); + @override + _i15.Coin get coin => (super.noSuchMethod( + Invocation.getter(#coin), + returnValue: _i15.Coin.bitcoin, + ) as _i15.Coin); + @override + _i16.Future> get allOwnAddresses => (super.noSuchMethod( + Invocation.getter(#allOwnAddresses), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + _i16.Future<_i9.UtxoData> get utxoData => (super.noSuchMethod( + Invocation.getter(#utxoData), + returnValue: _i16.Future<_i9.UtxoData>.value(_FakeUtxoData_6( + this, + Invocation.getter(#utxoData), + )), + ) as _i16.Future<_i9.UtxoData>); + @override + _i16.Future> get unspentOutputs => (super.noSuchMethod( + Invocation.getter(#unspentOutputs), + returnValue: + _i16.Future>.value(<_i9.UtxoObject>[]), + ) as _i16.Future>); + @override + _i16.Future<_i10.Decimal> get availableBalance => (super.noSuchMethod( + Invocation.getter(#availableBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#availableBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get pendingBalance => (super.noSuchMethod( + Invocation.getter(#pendingBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#pendingBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get balanceMinusMaxFee => (super.noSuchMethod( + Invocation.getter(#balanceMinusMaxFee), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#balanceMinusMaxFee), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get totalBalance => (super.noSuchMethod( + Invocation.getter(#totalBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#totalBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future get currentReceivingAddress => (super.noSuchMethod( + Invocation.getter(#currentReceivingAddress), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future get currentLegacyReceivingAddress => (super.noSuchMethod( + Invocation.getter(#currentLegacyReceivingAddress), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future get currentReceivingAddressP2SH => (super.noSuchMethod( + Invocation.getter(#currentReceivingAddressP2SH), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + bool get hasCalledExit => (super.noSuchMethod( + Invocation.getter(#hasCalledExit), + returnValue: false, + ) as bool); + @override + _i16.Future<_i9.FeeObject> get fees => (super.noSuchMethod( + Invocation.getter(#fees), + returnValue: _i16.Future<_i9.FeeObject>.value(_FakeFeeObject_8( + this, + Invocation.getter(#fees), + )), + ) as _i16.Future<_i9.FeeObject>); + @override + _i16.Future get maxFee => (super.noSuchMethod( + Invocation.getter(#maxFee), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future> get mnemonic => (super.noSuchMethod( + Invocation.getter(#mnemonic), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + _i16.Future get chainHeight => (super.noSuchMethod( + Invocation.getter(#chainHeight), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + int get storedChainHeight => (super.noSuchMethod( + Invocation.getter(#storedChainHeight), + returnValue: 0, + ) as int); + @override + bool get shouldAutoSync => (super.noSuchMethod( + Invocation.getter(#shouldAutoSync), + returnValue: false, + ) as bool); + @override + set shouldAutoSync(bool? shouldAutoSync) => super.noSuchMethod( + Invocation.setter( + #shouldAutoSync, + shouldAutoSync, + ), + returnValueForMissingStub: null, + ); + @override + bool get isRefreshing => (super.noSuchMethod( + Invocation.getter(#isRefreshing), + returnValue: false, + ) as bool); + @override + bool get isConnected => (super.noSuchMethod( + Invocation.getter(#isConnected), + returnValue: false, + ) as bool); + @override + _i16.Future<_i9.TransactionData> get transactionData => (super.noSuchMethod( + Invocation.getter(#transactionData), + returnValue: + _i16.Future<_i9.TransactionData>.value(_FakeTransactionData_9( + this, + Invocation.getter(#transactionData), + )), + ) as _i16.Future<_i9.TransactionData>); + @override + String get walletId => (super.noSuchMethod( + Invocation.getter(#walletId), + returnValue: '', + ) as String); + @override + String get walletName => (super.noSuchMethod( + Invocation.getter(#walletName), + returnValue: '', + ) as String); + @override + set walletName(String? newName) => super.noSuchMethod( + Invocation.setter( + #walletName, + newName, + ), + returnValueForMissingStub: null, + ); + @override + _i11.ElectrumX get electrumXClient => (super.noSuchMethod( + Invocation.getter(#electrumXClient), + returnValue: _FakeElectrumX_10( + this, + Invocation.getter(#electrumXClient), + ), + ) as _i11.ElectrumX); + @override + _i12.CachedElectrumX get cachedElectrumXClient => (super.noSuchMethod( + Invocation.getter(#cachedElectrumXClient), + returnValue: _FakeCachedElectrumX_11( + this, + Invocation.getter(#cachedElectrumXClient), + ), + ) as _i12.CachedElectrumX); + @override + set onIsActiveWalletChanged(void Function(bool)? _onIsActiveWalletChanged) => + super.noSuchMethod( + Invocation.setter( + #onIsActiveWalletChanged, + _onIsActiveWalletChanged, + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future exit() => (super.noSuchMethod( + Invocation.method( + #exit, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future updateStoredChainHeight({required int? newHeight}) => + (super.noSuchMethod( + Invocation.method( + #updateStoredChainHeight, + [], + {#newHeight: newHeight}, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i20.DerivePathType addressType({required String? address}) => + (super.noSuchMethod( + Invocation.method( + #addressType, + [], + {#address: address}, + ), + returnValue: _i20.DerivePathType.bip44, + ) as _i20.DerivePathType); + @override + _i16.Future recoverFromMnemonic({ + required String? mnemonic, + required int? maxUnusedAddressGap, + required int? maxNumberOfIndexesToCheck, + required int? height, + }) => + (super.noSuchMethod( + Invocation.method( + #recoverFromMnemonic, + [], + { + #mnemonic: mnemonic, + #maxUnusedAddressGap: maxUnusedAddressGap, + #maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck, + #height: height, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future getTransactionCacheEarly(List? allAddresses) => + (super.noSuchMethod( + Invocation.method( + #getTransactionCacheEarly, + [allAddresses], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future refreshIfThereIsNewData() => (super.noSuchMethod( + Invocation.method( + #refreshIfThereIsNewData, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future getAllTxsToWatch(_i9.TransactionData? txData) => + (super.noSuchMethod( + Invocation.method( + #getAllTxsToWatch, + [txData], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future refresh() => (super.noSuchMethod( + Invocation.method( + #refresh, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future> prepareSend({ + required String? address, + required int? satoshiAmount, + Map? args, + }) => + (super.noSuchMethod( + Invocation.method( + #prepareSend, + [], + { + #address: address, + #satoshiAmount: satoshiAmount, + #args: args, + }, + ), + returnValue: + _i16.Future>.value({}), + ) as _i16.Future>); + @override + _i16.Future confirmSend({required Map? txData}) => + (super.noSuchMethod( + Invocation.method( + #confirmSend, + [], + {#txData: txData}, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future send({ + required String? toAddress, + required int? amount, + Map? args = const {}, + }) => + (super.noSuchMethod( + Invocation.method( + #send, + [], + { + #toAddress: toAddress, + #amount: amount, + #args: args, + }, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future testNetworkConnection() => (super.noSuchMethod( + Invocation.method( + #testNetworkConnection, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + void startNetworkAlivePinging() => super.noSuchMethod( + Invocation.method( + #startNetworkAlivePinging, + [], + ), + returnValueForMissingStub: null, + ); + @override + void stopNetworkAlivePinging() => super.noSuchMethod( + Invocation.method( + #stopNetworkAlivePinging, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future initializeNew() => (super.noSuchMethod( + Invocation.method( + #initializeNew, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future initializeExisting() => (super.noSuchMethod( + Invocation.method( + #initializeExisting, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + bool validateAddress(String? address) => (super.noSuchMethod( + Invocation.method( + #validateAddress, + [address], + ), + returnValue: false, + ) as bool); + @override + _i16.Future updateNode(bool? shouldRefresh) => (super.noSuchMethod( + Invocation.method( + #updateNode, + [shouldRefresh], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future<_i11.ElectrumXNode> getCurrentNode() => (super.noSuchMethod( + Invocation.method( + #getCurrentNode, + [], + ), + returnValue: + _i16.Future<_i11.ElectrumXNode>.value(_FakeElectrumXNode_12( + this, + Invocation.method( + #getCurrentNode, + [], + ), + )), + ) as _i16.Future<_i11.ElectrumXNode>); + @override + _i16.Future addDerivation({ + required int? chain, + required String? address, + required String? pubKey, + required String? wif, + required _i20.DerivePathType? derivePathType, + }) => + (super.noSuchMethod( + Invocation.method( + #addDerivation, + [], + { + #chain: chain, + #address: address, + #pubKey: pubKey, + #wif: wif, + #derivePathType: derivePathType, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future addDerivations({ + required int? chain, + required _i20.DerivePathType? derivePathType, + required Map? derivationsToAdd, + }) => + (super.noSuchMethod( + Invocation.method( + #addDerivations, + [], + { + #chain: chain, + #derivePathType: derivePathType, + #derivationsToAdd: derivationsToAdd, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future getTxCount({required String? address}) => + (super.noSuchMethod( + Invocation.method( + #getTxCount, + [], + {#address: address}, + ), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future checkCurrentReceivingAddressesForTransactions() => + (super.noSuchMethod( + Invocation.method( + #checkCurrentReceivingAddressesForTransactions, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future checkCurrentChangeAddressesForTransactions() => + (super.noSuchMethod( + Invocation.method( + #checkCurrentChangeAddressesForTransactions, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future>> fastFetch( + List? allTxHashes) => + (super.noSuchMethod( + Invocation.method( + #fastFetch, + [allTxHashes], + ), + returnValue: _i16.Future>>.value( + >[]), + ) as _i16.Future>>); + @override + int estimateTxFee({ + required int? vSize, + required int? feeRatePerKB, + }) => + (super.noSuchMethod( + Invocation.method( + #estimateTxFee, + [], + { + #vSize: vSize, + #feeRatePerKB: feeRatePerKB, + }, + ), + returnValue: 0, + ) as int); + @override + dynamic coinSelection( + int? satoshiAmountToSend, + int? selectedTxFeeRate, + String? _recipientAddress, + bool? isSendAll, { + int? additionalOutputs = 0, + List<_i9.UtxoObject>? utxos, + }) => + super.noSuchMethod(Invocation.method( + #coinSelection, + [ + satoshiAmountToSend, + selectedTxFeeRate, + _recipientAddress, + isSendAll, + ], + { + #additionalOutputs: additionalOutputs, + #utxos: utxos, + }, + )); + @override + _i16.Future> fetchBuildTxData( + List<_i9.UtxoObject>? utxosToUse) => + (super.noSuchMethod( + Invocation.method( + #fetchBuildTxData, + [utxosToUse], + ), + returnValue: + _i16.Future>.value({}), + ) as _i16.Future>); + @override + _i16.Future> buildTransaction({ + required List<_i9.UtxoObject>? utxosToUse, + required Map? utxoSigningData, + required List? recipients, + required List? satoshiAmounts, + }) => + (super.noSuchMethod( + Invocation.method( + #buildTransaction, + [], + { + #utxosToUse: utxosToUse, + #utxoSigningData: utxoSigningData, + #recipients: recipients, + #satoshiAmounts: satoshiAmounts, + }, + ), + returnValue: + _i16.Future>.value({}), + ) as _i16.Future>); + @override + _i16.Future fullRescan( + int? maxUnusedAddressGap, + int? maxNumberOfIndexesToCheck, + ) => + (super.noSuchMethod( + Invocation.method( + #fullRescan, + [ + maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future estimateFeeFor( + int? satoshiAmount, + int? feeRate, + ) => + (super.noSuchMethod( + Invocation.method( + #estimateFeeFor, + [ + satoshiAmount, + feeRate, + ], + ), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + int roughFeeEstimate( + int? inputCount, + int? outputCount, + int? feeRatePerKB, + ) => + (super.noSuchMethod( + Invocation.method( + #roughFeeEstimate, + [ + inputCount, + outputCount, + feeRatePerKB, + ], + ), + returnValue: 0, + ) as int); + @override + int sweepAllEstimate(int? feeRate) => (super.noSuchMethod( + Invocation.method( + #sweepAllEstimate, + [feeRate], + ), + returnValue: 0, + ) as int); + @override + _i16.Future generateNewAddress() => (super.noSuchMethod( + Invocation.method( + #generateNewAddress, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); +} + +/// A class which mocks [LocaleService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockLocaleService extends _i1.Mock implements _i21.LocaleService { + MockLocaleService() { + _i1.throwOnMissingStub(this); + } + + @override + String get locale => (super.noSuchMethod( + Invocation.getter(#locale), + returnValue: '', + ) as String); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + _i16.Future loadLocale({bool? notify = true}) => (super.noSuchMethod( + Invocation.method( + #loadLocale, + [], + {#notify: notify}, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Prefs]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPrefs extends _i1.Mock implements _i17.Prefs { + MockPrefs() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isInitialized => (super.noSuchMethod( + Invocation.getter(#isInitialized), + returnValue: false, + ) as bool); + @override + int get lastUnlockedTimeout => (super.noSuchMethod( + Invocation.getter(#lastUnlockedTimeout), + returnValue: 0, + ) as int); + @override + set lastUnlockedTimeout(int? lastUnlockedTimeout) => super.noSuchMethod( + Invocation.setter( + #lastUnlockedTimeout, + lastUnlockedTimeout, + ), + returnValueForMissingStub: null, + ); + @override + int get lastUnlocked => (super.noSuchMethod( + Invocation.getter(#lastUnlocked), + returnValue: 0, + ) as int); + @override + set lastUnlocked(int? lastUnlocked) => super.noSuchMethod( + Invocation.setter( + #lastUnlocked, + lastUnlocked, + ), + returnValueForMissingStub: null, + ); + @override + int get currentNotificationId => (super.noSuchMethod( + Invocation.getter(#currentNotificationId), + returnValue: 0, + ) as int); + @override + List get walletIdsSyncOnStartup => (super.noSuchMethod( + Invocation.getter(#walletIdsSyncOnStartup), + returnValue: [], + ) as List); + @override + set walletIdsSyncOnStartup(List? walletIdsSyncOnStartup) => + super.noSuchMethod( + Invocation.setter( + #walletIdsSyncOnStartup, + walletIdsSyncOnStartup, + ), + returnValueForMissingStub: null, + ); + @override + _i22.SyncingType get syncType => (super.noSuchMethod( + Invocation.getter(#syncType), + returnValue: _i22.SyncingType.currentWalletOnly, + ) as _i22.SyncingType); + @override + set syncType(_i22.SyncingType? syncType) => super.noSuchMethod( + Invocation.setter( + #syncType, + syncType, + ), + returnValueForMissingStub: null, + ); + @override + bool get wifiOnly => (super.noSuchMethod( + Invocation.getter(#wifiOnly), + returnValue: false, + ) as bool); + @override + set wifiOnly(bool? wifiOnly) => super.noSuchMethod( + Invocation.setter( + #wifiOnly, + wifiOnly, + ), + returnValueForMissingStub: null, + ); + @override + bool get showFavoriteWallets => (super.noSuchMethod( + Invocation.getter(#showFavoriteWallets), + returnValue: false, + ) as bool); + @override + set showFavoriteWallets(bool? showFavoriteWallets) => super.noSuchMethod( + Invocation.setter( + #showFavoriteWallets, + showFavoriteWallets, + ), + returnValueForMissingStub: null, + ); + @override + String get language => (super.noSuchMethod( + Invocation.getter(#language), + returnValue: '', + ) as String); + @override + set language(String? newLanguage) => super.noSuchMethod( + Invocation.setter( + #language, + newLanguage, + ), + returnValueForMissingStub: null, + ); + @override + String get currency => (super.noSuchMethod( + Invocation.getter(#currency), + returnValue: '', + ) as String); + @override + set currency(String? newCurrency) => super.noSuchMethod( + Invocation.setter( + #currency, + newCurrency, + ), + returnValueForMissingStub: null, + ); + @override + _i23.ExchangeRateType get exchangeRateType => (super.noSuchMethod( + Invocation.getter(#exchangeRateType), + returnValue: _i23.ExchangeRateType.estimated, + ) as _i23.ExchangeRateType); + @override + set exchangeRateType(_i23.ExchangeRateType? exchangeRateType) => + super.noSuchMethod( + Invocation.setter( + #exchangeRateType, + exchangeRateType, + ), + returnValueForMissingStub: null, + ); + @override + bool get useBiometrics => (super.noSuchMethod( + Invocation.getter(#useBiometrics), + returnValue: false, + ) as bool); + @override + set useBiometrics(bool? useBiometrics) => super.noSuchMethod( + Invocation.setter( + #useBiometrics, + useBiometrics, + ), + returnValueForMissingStub: null, + ); + @override + bool get hasPin => (super.noSuchMethod( + Invocation.getter(#hasPin), + returnValue: false, + ) as bool); + @override + set hasPin(bool? hasPin) => super.noSuchMethod( + Invocation.setter( + #hasPin, + hasPin, + ), + returnValueForMissingStub: null, + ); + @override + bool get showTestNetCoins => (super.noSuchMethod( + Invocation.getter(#showTestNetCoins), + returnValue: false, + ) as bool); + @override + set showTestNetCoins(bool? showTestNetCoins) => super.noSuchMethod( + Invocation.setter( + #showTestNetCoins, + showTestNetCoins, + ), + returnValueForMissingStub: null, + ); + @override + bool get isAutoBackupEnabled => (super.noSuchMethod( + Invocation.getter(#isAutoBackupEnabled), + returnValue: false, + ) as bool); + @override + set isAutoBackupEnabled(bool? isAutoBackupEnabled) => super.noSuchMethod( + Invocation.setter( + #isAutoBackupEnabled, + isAutoBackupEnabled, + ), + returnValueForMissingStub: null, + ); + @override + set autoBackupLocation(String? autoBackupLocation) => super.noSuchMethod( + Invocation.setter( + #autoBackupLocation, + autoBackupLocation, + ), + returnValueForMissingStub: null, + ); + @override + _i24.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod( + Invocation.getter(#backupFrequencyType), + returnValue: _i24.BackupFrequencyType.everyTenMinutes, + ) as _i24.BackupFrequencyType); + @override + set backupFrequencyType(_i24.BackupFrequencyType? backupFrequencyType) => + super.noSuchMethod( + Invocation.setter( + #backupFrequencyType, + backupFrequencyType, + ), + returnValueForMissingStub: null, + ); + @override + set lastAutoBackup(DateTime? lastAutoBackup) => super.noSuchMethod( + Invocation.setter( + #lastAutoBackup, + lastAutoBackup, + ), + returnValueForMissingStub: null, + ); + @override + bool get hideBlockExplorerWarning => (super.noSuchMethod( + Invocation.getter(#hideBlockExplorerWarning), + returnValue: false, + ) as bool); + @override + set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) => + super.noSuchMethod( + Invocation.setter( + #hideBlockExplorerWarning, + hideBlockExplorerWarning, + ), + returnValueForMissingStub: null, + ); + @override + bool get gotoWalletOnStartup => (super.noSuchMethod( + Invocation.getter(#gotoWalletOnStartup), + returnValue: false, + ) as bool); + @override + set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod( + Invocation.setter( + #gotoWalletOnStartup, + gotoWalletOnStartup, + ), + returnValueForMissingStub: null, + ); + @override + set startupWalletId(String? startupWalletId) => super.noSuchMethod( + Invocation.setter( + #startupWalletId, + startupWalletId, + ), + returnValueForMissingStub: null, + ); + @override + bool get externalCalls => (super.noSuchMethod( + Invocation.getter(#externalCalls), + returnValue: false, + ) as bool); + @override + set externalCalls(bool? externalCalls) => super.noSuchMethod( + Invocation.setter( + #externalCalls, + externalCalls, + ), + returnValueForMissingStub: null, + ); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + _i16.Future init() => (super.noSuchMethod( + Invocation.method( + #init, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future incrementCurrentNotificationIndex() => (super.noSuchMethod( + Invocation.method( + #incrementCurrentNotificationIndex, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Manager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManager extends _i1.Mock implements _i6.Manager { + @override + bool get isActiveWallet => (super.noSuchMethod( + Invocation.getter(#isActiveWallet), + returnValue: false, + ) as bool); + @override + set isActiveWallet(bool? isActive) => super.noSuchMethod( + Invocation.setter( + #isActiveWallet, + isActive, + ), + returnValueForMissingStub: null, + ); + @override + _i13.CoinServiceAPI get wallet => (super.noSuchMethod( + Invocation.getter(#wallet), + returnValue: _FakeCoinServiceAPI_13( + this, + Invocation.getter(#wallet), + ), + ) as _i13.CoinServiceAPI); + @override + bool get hasBackgroundRefreshListener => (super.noSuchMethod( + Invocation.getter(#hasBackgroundRefreshListener), + returnValue: false, + ) as bool); + @override + _i15.Coin get coin => (super.noSuchMethod( + Invocation.getter(#coin), + returnValue: _i15.Coin.bitcoin, + ) as _i15.Coin); + @override + bool get isRefreshing => (super.noSuchMethod( + Invocation.getter(#isRefreshing), + returnValue: false, + ) as bool); + @override + bool get shouldAutoSync => (super.noSuchMethod( + Invocation.getter(#shouldAutoSync), + returnValue: false, + ) as bool); + @override + set shouldAutoSync(bool? shouldAutoSync) => super.noSuchMethod( + Invocation.setter( + #shouldAutoSync, + shouldAutoSync, + ), + returnValueForMissingStub: null, + ); + @override + bool get isFavorite => (super.noSuchMethod( + Invocation.getter(#isFavorite), + returnValue: false, + ) as bool); + @override + set isFavorite(bool? markFavorite) => super.noSuchMethod( + Invocation.setter( + #isFavorite, + markFavorite, + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future<_i9.FeeObject> get fees => (super.noSuchMethod( + Invocation.getter(#fees), + returnValue: _i16.Future<_i9.FeeObject>.value(_FakeFeeObject_8( + this, + Invocation.getter(#fees), + )), + ) as _i16.Future<_i9.FeeObject>); + @override + _i16.Future get maxFee => (super.noSuchMethod( + Invocation.getter(#maxFee), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future get currentReceivingAddress => (super.noSuchMethod( + Invocation.getter(#currentReceivingAddress), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future<_i10.Decimal> get availableBalance => (super.noSuchMethod( + Invocation.getter(#availableBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#availableBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i10.Decimal get cachedAvailableBalance => (super.noSuchMethod( + Invocation.getter(#cachedAvailableBalance), + returnValue: _FakeDecimal_7( + this, + Invocation.getter(#cachedAvailableBalance), + ), + ) as _i10.Decimal); + @override + _i16.Future<_i10.Decimal> get pendingBalance => (super.noSuchMethod( + Invocation.getter(#pendingBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#pendingBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get balanceMinusMaxFee => (super.noSuchMethod( + Invocation.getter(#balanceMinusMaxFee), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#balanceMinusMaxFee), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get totalBalance => (super.noSuchMethod( + Invocation.getter(#totalBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#totalBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i10.Decimal get cachedTotalBalance => (super.noSuchMethod( + Invocation.getter(#cachedTotalBalance), + returnValue: _FakeDecimal_7( + this, + Invocation.getter(#cachedTotalBalance), + ), + ) as _i10.Decimal); + @override + _i16.Future> get allOwnAddresses => (super.noSuchMethod( + Invocation.getter(#allOwnAddresses), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + _i16.Future<_i9.TransactionData> get transactionData => (super.noSuchMethod( + Invocation.getter(#transactionData), + returnValue: + _i16.Future<_i9.TransactionData>.value(_FakeTransactionData_9( + this, + Invocation.getter(#transactionData), + )), + ) as _i16.Future<_i9.TransactionData>); + @override + _i16.Future> get unspentOutputs => (super.noSuchMethod( + Invocation.getter(#unspentOutputs), + returnValue: + _i16.Future>.value(<_i9.UtxoObject>[]), + ) as _i16.Future>); + @override + set walletName(String? newName) => super.noSuchMethod( + Invocation.setter( + #walletName, + newName, + ), + returnValueForMissingStub: null, + ); + @override + String get walletName => (super.noSuchMethod( + Invocation.getter(#walletName), + returnValue: '', + ) as String); + @override + String get walletId => (super.noSuchMethod( + Invocation.getter(#walletId), + returnValue: '', + ) as String); + @override + _i16.Future> get mnemonic => (super.noSuchMethod( + Invocation.getter(#mnemonic), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + bool get isConnected => (super.noSuchMethod( + Invocation.getter(#isConnected), + returnValue: false, + ) as bool); + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + ) as bool); + @override + _i16.Future updateNode(bool? shouldRefresh) => (super.noSuchMethod( + Invocation.method( + #updateNode, + [shouldRefresh], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future> prepareSend({ + required String? address, + required int? satoshiAmount, + Map? args, + }) => + (super.noSuchMethod( + Invocation.method( + #prepareSend, + [], + { + #address: address, + #satoshiAmount: satoshiAmount, + #args: args, + }, + ), + returnValue: + _i16.Future>.value({}), + ) as _i16.Future>); + @override + _i16.Future confirmSend({required Map? txData}) => + (super.noSuchMethod( + Invocation.method( + #confirmSend, + [], + {#txData: txData}, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future send({ + required String? toAddress, + required int? amount, + Map? args = const {}, + }) => + (super.noSuchMethod( + Invocation.method( + #send, + [], + { + #toAddress: toAddress, + #amount: amount, + #args: args, + }, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future refresh() => (super.noSuchMethod( + Invocation.method( + #refresh, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + bool validateAddress(String? address) => (super.noSuchMethod( + Invocation.method( + #validateAddress, + [address], + ), + returnValue: false, + ) as bool); + @override + _i16.Future testNetworkConnection() => (super.noSuchMethod( + Invocation.method( + #testNetworkConnection, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future initializeNew() => (super.noSuchMethod( + Invocation.method( + #initializeNew, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future initializeExisting() => (super.noSuchMethod( + Invocation.method( + #initializeExisting, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future recoverFromMnemonic({ + required String? mnemonic, + required int? maxUnusedAddressGap, + required int? maxNumberOfIndexesToCheck, + required int? height, + }) => + (super.noSuchMethod( + Invocation.method( + #recoverFromMnemonic, + [], + { + #mnemonic: mnemonic, + #maxUnusedAddressGap: maxUnusedAddressGap, + #maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck, + #height: height, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future exitCurrentWallet() => (super.noSuchMethod( + Invocation.method( + #exitCurrentWallet, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future fullRescan( + int? maxUnusedAddressGap, + int? maxNumberOfIndexesToCheck, + ) => + (super.noSuchMethod( + Invocation.method( + #fullRescan, + [ + maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future isOwnAddress(String? address) => (super.noSuchMethod( + Invocation.method( + #isOwnAddress, + [address], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future estimateFeeFor( + int? satoshiAmount, + int? feeRate, + ) => + (super.noSuchMethod( + Invocation.method( + #estimateFeeFor, + [ + satoshiAmount, + feeRate, + ], + ), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future generateNewAddress() => (super.noSuchMethod( + Invocation.method( + #generateNewAddress, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + void addListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void removeListener(_i18.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [CoinServiceAPI]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCoinServiceAPI extends _i1.Mock implements _i13.CoinServiceAPI { + @override + set onIsActiveWalletChanged(void Function(bool)? _onIsActiveWalletChanged) => + super.noSuchMethod( + Invocation.setter( + #onIsActiveWalletChanged, + _onIsActiveWalletChanged, + ), + returnValueForMissingStub: null, + ); + @override + _i15.Coin get coin => (super.noSuchMethod( + Invocation.getter(#coin), + returnValue: _i15.Coin.bitcoin, + ) as _i15.Coin); + @override + bool get isRefreshing => (super.noSuchMethod( + Invocation.getter(#isRefreshing), + returnValue: false, + ) as bool); + @override + bool get shouldAutoSync => (super.noSuchMethod( + Invocation.getter(#shouldAutoSync), + returnValue: false, + ) as bool); + @override + set shouldAutoSync(bool? shouldAutoSync) => super.noSuchMethod( + Invocation.setter( + #shouldAutoSync, + shouldAutoSync, + ), + returnValueForMissingStub: null, + ); + @override + bool get isFavorite => (super.noSuchMethod( + Invocation.getter(#isFavorite), + returnValue: false, + ) as bool); + @override + set isFavorite(bool? markFavorite) => super.noSuchMethod( + Invocation.setter( + #isFavorite, + markFavorite, + ), + returnValueForMissingStub: null, + ); + @override + _i16.Future<_i9.FeeObject> get fees => (super.noSuchMethod( + Invocation.getter(#fees), + returnValue: _i16.Future<_i9.FeeObject>.value(_FakeFeeObject_8( + this, + Invocation.getter(#fees), + )), + ) as _i16.Future<_i9.FeeObject>); + @override + _i16.Future get maxFee => (super.noSuchMethod( + Invocation.getter(#maxFee), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future get currentReceivingAddress => (super.noSuchMethod( + Invocation.getter(#currentReceivingAddress), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future<_i10.Decimal> get availableBalance => (super.noSuchMethod( + Invocation.getter(#availableBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#availableBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get pendingBalance => (super.noSuchMethod( + Invocation.getter(#pendingBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#pendingBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get totalBalance => (super.noSuchMethod( + Invocation.getter(#totalBalance), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#totalBalance), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future<_i10.Decimal> get balanceMinusMaxFee => (super.noSuchMethod( + Invocation.getter(#balanceMinusMaxFee), + returnValue: _i16.Future<_i10.Decimal>.value(_FakeDecimal_7( + this, + Invocation.getter(#balanceMinusMaxFee), + )), + ) as _i16.Future<_i10.Decimal>); + @override + _i16.Future> get allOwnAddresses => (super.noSuchMethod( + Invocation.getter(#allOwnAddresses), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + _i16.Future<_i9.TransactionData> get transactionData => (super.noSuchMethod( + Invocation.getter(#transactionData), + returnValue: + _i16.Future<_i9.TransactionData>.value(_FakeTransactionData_9( + this, + Invocation.getter(#transactionData), + )), + ) as _i16.Future<_i9.TransactionData>); + @override + _i16.Future> get unspentOutputs => (super.noSuchMethod( + Invocation.getter(#unspentOutputs), + returnValue: + _i16.Future>.value(<_i9.UtxoObject>[]), + ) as _i16.Future>); + @override + set walletName(String? newName) => super.noSuchMethod( + Invocation.setter( + #walletName, + newName, + ), + returnValueForMissingStub: null, + ); + @override + String get walletName => (super.noSuchMethod( + Invocation.getter(#walletName), + returnValue: '', + ) as String); + @override + String get walletId => (super.noSuchMethod( + Invocation.getter(#walletId), + returnValue: '', + ) as String); + @override + _i16.Future> get mnemonic => (super.noSuchMethod( + Invocation.getter(#mnemonic), + returnValue: _i16.Future>.value([]), + ) as _i16.Future>); + @override + bool get hasCalledExit => (super.noSuchMethod( + Invocation.getter(#hasCalledExit), + returnValue: false, + ) as bool); + @override + bool get isConnected => (super.noSuchMethod( + Invocation.getter(#isConnected), + returnValue: false, + ) as bool); + @override + _i16.Future> prepareSend({ + required String? address, + required int? satoshiAmount, + Map? args, + }) => + (super.noSuchMethod( + Invocation.method( + #prepareSend, + [], + { + #address: address, + #satoshiAmount: satoshiAmount, + #args: args, + }, + ), + returnValue: + _i16.Future>.value({}), + ) as _i16.Future>); + @override + _i16.Future confirmSend({required Map? txData}) => + (super.noSuchMethod( + Invocation.method( + #confirmSend, + [], + {#txData: txData}, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future send({ + required String? toAddress, + required int? amount, + Map? args, + }) => + (super.noSuchMethod( + Invocation.method( + #send, + [], + { + #toAddress: toAddress, + #amount: amount, + #args: args, + }, + ), + returnValue: _i16.Future.value(''), + ) as _i16.Future); + @override + _i16.Future refresh() => (super.noSuchMethod( + Invocation.method( + #refresh, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future updateNode(bool? shouldRefresh) => (super.noSuchMethod( + Invocation.method( + #updateNode, + [shouldRefresh], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + bool validateAddress(String? address) => (super.noSuchMethod( + Invocation.method( + #validateAddress, + [address], + ), + returnValue: false, + ) as bool); + @override + _i16.Future testNetworkConnection() => (super.noSuchMethod( + Invocation.method( + #testNetworkConnection, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); + @override + _i16.Future recoverFromMnemonic({ + required String? mnemonic, + required int? maxUnusedAddressGap, + required int? maxNumberOfIndexesToCheck, + required int? height, + }) => + (super.noSuchMethod( + Invocation.method( + #recoverFromMnemonic, + [], + { + #mnemonic: mnemonic, + #maxUnusedAddressGap: maxUnusedAddressGap, + #maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck, + #height: height, + }, + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future initializeNew() => (super.noSuchMethod( + Invocation.method( + #initializeNew, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future initializeExisting() => (super.noSuchMethod( + Invocation.method( + #initializeExisting, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future exit() => (super.noSuchMethod( + Invocation.method( + #exit, + [], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future fullRescan( + int? maxUnusedAddressGap, + int? maxNumberOfIndexesToCheck, + ) => + (super.noSuchMethod( + Invocation.method( + #fullRescan, + [ + maxUnusedAddressGap, + maxNumberOfIndexesToCheck, + ], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); + @override + _i16.Future estimateFeeFor( + int? satoshiAmount, + int? feeRate, + ) => + (super.noSuchMethod( + Invocation.method( + #estimateFeeFor, + [ + satoshiAmount, + feeRate, + ], + ), + returnValue: _i16.Future.value(0), + ) as _i16.Future); + @override + _i16.Future generateNewAddress() => (super.noSuchMethod( + Invocation.method( + #generateNewAddress, + [], + ), + returnValue: _i16.Future.value(false), + ) as _i16.Future); +} diff --git a/test/price_test.dart b/test/price_test.dart index b806bfd61..6abbb6741 100644 --- a/test/price_test.dart +++ b/test/price_test.dart @@ -16,6 +16,9 @@ void main() { setUp(() async { await setUpTestHive(); await Hive.openBox(DB.boxNamePriceCache); + await Hive.openBox(DB.boxNamePrefs); + await DB.instance.put( + boxName: DB.boxNamePrefs, key: "externalCalls", value: true); }); test("getPricesAnd24hChange fetch", () async { diff --git a/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart b/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart index a9a7fdaf7..4c392fe81 100644 --- a/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart +++ b/test/services/coins/bitcoincash/bitcoincash_wallet_test.dart @@ -26,7 +26,7 @@ import 'bitcoincash_wallet_test_parameters.dart'; void main() { group("bitcoincash constants", () { test("bitcoincash minimum confirmations", () async { - expect(MINIMUM_CONFIRMATIONS, 3); + expect(MINIMUM_CONFIRMATIONS, 1); }); test("bitcoincash dust limit", () async { expect(DUST_LIMIT, 546); @@ -831,18 +831,9 @@ void main() { await bch?.initializeNew(); await bch?.initializeExisting(); - expect( - Address.validateAddress( - await bch!.currentReceivingAddress, bitcoincashtestnet), - true); - expect( - Address.validateAddress( - await bch!.currentReceivingAddress, bitcoincashtestnet), - true); - expect( - Address.validateAddress( - await bch!.currentReceivingAddress, bitcoincashtestnet), - true); + expect(bch?.validateAddress(await bch!.currentReceivingAddress), true); + expect(bch?.validateAddress(await bch!.currentReceivingAddress), true); + expect(bch?.validateAddress(await bch!.currentReceivingAddress), true); verifyNever(client?.ping()).called(0); verify(client?.getServerFeatures()).called(1); @@ -884,8 +875,7 @@ void main() { expect(addresses?.length, 2); for (int i = 0; i < 2; i++) { - expect( - Address.validateAddress(addresses![i], bitcoincashtestnet), true); + expect(bch?.validateAddress(addresses![i]), true); } verifyNever(client?.ping()).called(0); diff --git a/test/widget_tests/address_book_card_test.dart b/test/widget_tests/address_book_card_test.dart index 664fe05f3..ef031eb4e 100644 --- a/test/widget_tests/address_book_card_test.dart +++ b/test/widget_tests/address_book_card_test.dart @@ -21,207 +21,46 @@ class MockedFunctions extends Mock { @GenerateMocks([AddressBookService]) void main() { - group('Navigation tests', () { - late AddressBookService service; - setUp(() { - service = MockAddressBookService(); + testWidgets('test returns Contact Address Entry', (widgetTester) async { + final service = MockAddressBookService(); - when(service.getContactById("some id")) - .thenAnswer((realInvocation) => Contact( - name: "John Doe", - addresses: [ - const ContactAddressEntry( - coin: Coin.bitcoincash, - address: "some bch address", - label: "Bills") - ], - isFavorite: true)); - }); + when(service.getContactById("default")) + .thenAnswer((realInvocation) => Contact( + name: "John Doe", + addresses: [ + const ContactAddressEntry( + coin: Coin.bitcoincash, + address: "some bch address", + label: "Bills") + ], + isFavorite: true)); - testWidgets('test returns Contact Address Entry', (widgetTester) async { - await widgetTester.pumpWidget( - ProviderScope( - overrides: [ - addressBookServiceProvider.overrideWithValue( - service, - ), - ], - child: MaterialApp( - theme: ThemeData( - extensions: [ - StackColors.fromStackColorTheme( - LightColors(), - ), - ], - ), - home: const AddressBookCard( - contactId: "some id", - ), + await widgetTester.pumpWidget( + ProviderScope( + overrides: [ + addressBookServiceProvider.overrideWithValue( + service, + ), + ], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: const AddressBookCard( + contactId: "default", ), ), - ); + ), + ); - expect(find.text("John Doe"), findsOneWidget); - expect(find.text(Coin.bitcoincash.ticker), findsOneWidget); - }); + expect(find.text("John Doe"), findsOneWidget); + expect(find.text("BCH"), findsOneWidget); + expect(find.text(Coin.bitcoincash.ticker), findsOneWidget); - // testWidgets("Test button press opens dialog", (widgetTester) async { - // // final service = MockAddressBookService(); - // - // when(service.getContactById("some id")) - // .thenAnswer((realInvocation) => Contact( - // name: "John Doe", - // addresses: [ - // const ContactAddressEntry( - // coin: Coin.bitcoincash, - // address: "some bch address", - // label: "Bills") - // ], - // isFavorite: true)); - // - // await widgetTester.pumpWidget( - // ProviderScope( - // overrides: [ - // addressBookServiceProvider.overrideWithValue( - // service, - // ), - // ], - // child: MaterialApp( - // theme: ThemeData( - // extensions: [ - // StackColors.fromStackColorTheme( - // LightColors(), - // ), - // ], - // ), - // home: const AddressBookCard( - // contactId: "some id", - // ), - // ), - // ), - // ); - // // - // // when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f")) - // // .thenAnswer((realInvocation) => Contact( - // // name: "John Doe", - // // addresses: [ - // // const ContactAddressEntry( - // // coin: Coin.bitcoincash, - // // address: "some bch address", - // // label: "Bills") - // // ], - // // isFavorite: true)); - // await widgetTester.tap(find.byType(RawMaterialButton)); - // // verify(MockedFunctions().showDialog()).called(1); - // await widgetTester.pump(); - // when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f")) - // .thenAnswer((realInvocation) => Contact( - // name: "John Doe", - // addresses: [ - // const ContactAddressEntry( - // coin: Coin.bitcoincash, - // address: "some bch address", - // label: "Bills") - // ], - // isFavorite: true)); - // - // expect( - // find.byWidget(const ContactPopUp( - // contactId: "03177ce0-4af4-11ed-9617-af8aa7a3796f")), - // findsOneWidget); - // // await widgetTester.pump(); - // // // when(contact) - // // await widgetTester.pump(); - // }); + await widgetTester.tap(find.byType(RawMaterialButton)); }); - - // testWidgets('test returns Contact Address Entry', (widgetTester) async { - // // final service = MockAddressBookService(); - // // when(service.getContactById("some id")) - // // .thenAnswer((realInvocation) => Contact( - // // name: "John Doe", - // // addresses: [ - // // const ContactAddressEntry( - // // coin: Coin.bitcoincash, - // // address: "some bch address", - // // label: "Bills") - // // ], - // // isFavorite: true)); - // - // await widgetTester.pumpWidget( - // ProviderScope( - // overrides: [ - // addressBookServiceProvider.overrideWithValue( - // serv, - // ), - // ], - // child: MaterialApp( - // theme: ThemeData( - // extensions: [ - // StackColors.fromStackColorTheme( - // LightColors(), - // ), - // ], - // ), - // home: const AddressBookCard( - // contactId: "some id", - // ), - // ), - // ), - // ); - // - // expect(find.text("John Doe"), findsOneWidget); - // expect(find.text(Coin.bitcoincash.ticker), findsOneWidget); - // }); - - // testWidgets("Test button press opens dialog", (widgetTester) async { - // final service = MockAddressBookService(); - // - // // when(service.getContactById("some id")) - // // .thenAnswer((realInvocation) => Contact( - // // name: "John Doe", - // // addresses: [ - // // const ContactAddressEntry( - // // coin: Coin.bitcoincash, - // // address: "some bch address", - // // label: "Bills") - // // ], - // // isFavorite: true)); - // - // await widgetTester.pumpWidget( - // ProviderScope( - // overrides: [ - // addressBookServiceProvider.overrideWithValue( - // service, - // ), - // ], - // child: MaterialApp( - // theme: ThemeData( - // extensions: [ - // StackColors.fromStackColorTheme( - // LightColors(), - // ), - // ], - // ), - // home: const AddressBookCard( - // contactId: "03177ce0-4af4-11ed-9617-af8aa7a3796f", - // ), - // ), - // ), - // ); - // // - // // when(service.getContactById("03177ce0-4af4-11ed-9617-af8aa7a3796f")) - // // .thenAnswer((realInvocation) => Contact( - // // name: "John Doe", - // // addresses: [ - // // const ContactAddressEntry( - // // coin: Coin.bitcoincash, - // // address: "some bch address", - // // label: "Bills") - // // ], - // // isFavorite: true)); - // // await widgetTester.tap(find.byType(RawMaterialButton)); - // // // when(contact) - // // await widgetTester.pump(); - // }); } diff --git a/test/widget_tests/custom_buttons/favorite_toggle_test.dart b/test/widget_tests/custom_buttons/favorite_toggle_test.dart new file mode 100644 index 000000000..54de9a44c --- /dev/null +++ b/test/widget_tests/custom_buttons/favorite_toggle_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/widgets/custom_buttons/favorite_toggle.dart'; + +void main() { + testWidgets("Test widget build", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + ProviderScope( + overrides: [], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: FavoriteToggle( + onChanged: null, + key: key, + ), + ), + ), + ); + + expect(find.byType(FavoriteToggle), findsOneWidget); + expect(find.byType(SvgPicture), findsOneWidget); + }); +} diff --git a/test/widget_tests/custom_loading_overlay_test.dart b/test/widget_tests/custom_loading_overlay_test.dart index 2e1ac4846..48939c9b4 100644 --- a/test/widget_tests/custom_loading_overlay_test.dart +++ b/test/widget_tests/custom_loading_overlay_test.dart @@ -1,3 +1,4 @@ +import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:stackwallet/utilities/theme/light_colors.dart'; @@ -6,8 +7,7 @@ import 'package:stackwallet/widgets/custom_loading_overlay.dart'; void main() { testWidgets("Test wiget displays correct text", (widgetTester) async { - const customLoadingOverlay = - CustomLoadingOverlay(message: "Updating exchange rate", eventBus: null); + final eventBus = EventBus(); await widgetTester.pumpWidget( MaterialApp( theme: ThemeData( @@ -15,8 +15,9 @@ void main() { StackColors.fromStackColorTheme(LightColors()), ], ), - home: const Material( - child: customLoadingOverlay, + home: Material( + child: CustomLoadingOverlay( + message: "Updating exchange rate", eventBus: eventBus), ), ), ); diff --git a/test/widget_tests/desktop/custom_text_button_test.dart b/test/widget_tests/desktop/custom_text_button_test.dart new file mode 100644 index 000000000..08cf19098 --- /dev/null +++ b/test/widget_tests/desktop/custom_text_button_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/custom_text_button.dart'; + +void main() { + testWidgets("Test text button ", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: CustomTextButtonBase( + key: key, + width: 200, + height: 300, + textButton: + const TextButton(onPressed: null, child: Text("Some Text")), + ), + ), + ), + ); + + expect(find.byType(CustomTextButtonBase), findsOneWidget); + }); +} diff --git a/test/widget_tests/desktop/desktop_app_bar_test.dart b/test/widget_tests/desktop/desktop_app_bar_test.dart new file mode 100644 index 000000000..2452f1860 --- /dev/null +++ b/test/widget_tests/desktop/desktop_app_bar_test.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; + +void main() { + testWidgets("Test DesktopAppBar widget", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: DesktopAppBar( + key: key, + isCompactHeight: false, + leading: const AppBarBackButton(), + trailing: const ExitToMyStackButton(), + center: const Text("Some Text"), + ), + ), + ), + ); + + expect(find.byType(DesktopAppBar), findsOneWidget); + }); +} diff --git a/test/widget_tests/desktop/desktop_dialog_close_button_test.dart b/test/widget_tests/desktop/desktop_dialog_close_button_test.dart new file mode 100644 index 000000000..4c9a8113f --- /dev/null +++ b/test/widget_tests/desktop/desktop_dialog_close_button_test.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart' as mockingjay; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; + +void main() { + testWidgets("test DesktopDialog button pressed", (widgetTester) async { + final key = UniqueKey(); + + final navigator = mockingjay.MockNavigator(); + + await widgetTester.pumpWidget( + ProviderScope( + overrides: [], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: mockingjay.MockNavigatorProvider( + navigator: navigator, + child: DesktopDialogCloseButton( + key: key, + onPressedOverride: null, + )), + ), + ), + ); + + await widgetTester.tap(find.byType(AppBarIconButton)); + await widgetTester.pumpAndSettle(); + + mockingjay.verify(() => navigator.pop()).called(1); + }); +} diff --git a/test/widget_tests/desktop/desktop_dialog_test.dart b/test/widget_tests/desktop/desktop_dialog_test.dart new file mode 100644 index 000000000..48e82bf89 --- /dev/null +++ b/test/widget_tests/desktop/desktop_dialog_test.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; + +void main() { + testWidgets("test DesktopDialog builds", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: DesktopDialog( + key: key, + child: const DesktopDialogCloseButton(), + ), + ), + ), + ); + + expect(find.byType(DesktopDialog), findsOneWidget); + }); +} diff --git a/test/widget_tests/desktop/desktop_scaffold_test.dart b/test/widget_tests/desktop/desktop_scaffold_test.dart new file mode 100644 index 000000000..b7eba2c6d --- /dev/null +++ b/test/widget_tests/desktop/desktop_scaffold_test.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; + +void main() { + testWidgets("test DesktopScaffold", (widgetTester) async { + final key = UniqueKey(); + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: DesktopScaffold( + key: key, + body: const SizedBox(), + ), + ), + ), + ); + }); + + testWidgets("Test MasterScaffold for non desktop", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: MasterScaffold( + key: key, + body: const SizedBox(), + appBar: AppBar(), + isDesktop: false, + ), + ), + ), + ); + }); + + testWidgets("Test MasterScaffold for desktop", (widgetTester) async { + final key = UniqueKey(); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: MasterScaffold( + key: key, + body: const SizedBox(), + appBar: AppBar(), + isDesktop: true, + ), + ), + ), + ); + }); +} diff --git a/test/widget_tests/icon_widgets/addressbook_icon_test.dart b/test/widget_tests/icon_widgets/addressbook_icon_test.dart new file mode 100644 index 000000000..89ff4f236 --- /dev/null +++ b/test/widget_tests/icon_widgets/addressbook_icon_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart'; + +void main() { + testWidgets("test address book icon widget", (widgetTester) async { + final key = UniqueKey(); + final addressBookIcon = AddressBookIcon( + key: key, + ); + + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: addressBookIcon, + ), + ), + ); + + expect(find.byWidget(addressBookIcon), findsOneWidget); + expect(find.byType(SvgPicture), findsOneWidget); + }); +} diff --git a/test/widget_tests/managed_favorite_test.dart b/test/widget_tests/managed_favorite_test.dart index 4caaad89d..dc643e831 100644 --- a/test/widget_tests/managed_favorite_test.dart +++ b/test/widget_tests/managed_favorite_test.dart @@ -18,6 +18,7 @@ import 'package:stackwallet/widgets/managed_favorite.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/theme/light_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; import 'managed_favorite_test.mocks.dart'; @@ -45,7 +46,8 @@ void main() { .thenAnswer((realInvocation) => manager); when(manager.isFavorite).thenAnswer((realInvocation) => false); - const managedFavorite = ManagedFavorite(walletId: "some wallet id"); + final key = UniqueKey(); + // const managedFavorite = ManagedFavorite(walletId: "some wallet id", key: key,); await widgetTester.pumpWidget( ProviderScope( overrides: [ @@ -59,8 +61,11 @@ void main() { ), ], ), - home: const Material( - child: managedFavorite, + home: Material( + child: ManagedFavorite( + walletId: "some wallet id", + key: key, + ), ), ), ), diff --git a/test/widget_tests/node_card_test.dart b/test/widget_tests/node_card_test.dart index 47209db58..2728fc304 100644 --- a/test/widget_tests/node_card_test.dart +++ b/test/widget_tests/node_card_test.dart @@ -82,7 +82,7 @@ void main() { (realInvocation) => NodeModel( host: "127.0.0.1", port: 2000, - name: "Stack Default", + name: "Some other node name", id: "node id", useSSL: true, enabled: true, @@ -94,7 +94,7 @@ void main() { NodeModel( host: "127.0.0.1", port: 2000, - name: "Stack Default", + name: "Some other node name", id: "node id", useSSL: true, enabled: true, @@ -122,7 +122,7 @@ void main() { ); await tester.pumpAndSettle(); - expect(find.text("Stack Default"), findsOneWidget); + expect(find.text("Some other node name"), findsOneWidget); expect(find.text("Connected"), findsOneWidget); expect(find.byType(Text), findsNWidgets(2)); expect(find.byType(SvgPicture), findsWidgets); diff --git a/test/widget_tests/node_options_sheet_test.dart b/test/widget_tests/node_options_sheet_test.dart index 78800fb8a..2a4782c07 100644 --- a/test/widget_tests/node_options_sheet_test.dart +++ b/test/widget_tests/node_options_sheet_test.dart @@ -29,7 +29,7 @@ void main() { (realInvocation) => NodeModel( host: "127.0.0.1", port: 2000, - name: "Stack Default", + name: "Some other name", id: "node id", useSSL: true, enabled: true, @@ -41,7 +41,7 @@ void main() { (realInvocation) => NodeModel( host: "127.0.0.1", port: 2000, - name: "Stack Default", + name: "Some other name", id: "node id", useSSL: true, enabled: true, @@ -72,7 +72,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text("Node options"), findsOneWidget); - expect(find.text("Stack Default"), findsOneWidget); + expect(find.text("Some other name"), findsOneWidget); expect(find.text("Connected"), findsOneWidget); expect(find.byType(SvgPicture), findsNWidgets(2)); expect(find.text("Details"), findsOneWidget); @@ -204,7 +204,6 @@ void main() { await tester.pumpAndSettle(); expect(find.text("Node options"), findsOneWidget); - // expect(find.text("Stack Default"), findsOneWidget); expect(find.text("Disconnected"), findsOneWidget); await tester.tap(find.text("Connect")); diff --git a/test/widget_tests/shake/shake_test.dart b/test/widget_tests/shake/shake_test.dart new file mode 100644 index 000000000..cddd99c05 --- /dev/null +++ b/test/widget_tests/shake/shake_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/shake/shake.dart'; + +void main() { + testWidgets("Widget build", (widgetTester) async { + await widgetTester.pumpWidget( + MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme(LightColors()), + ], + ), + home: Material( + child: Shake( + animationRange: 10, + controller: ShakeController(), + animationDuration: const Duration(milliseconds: 200), + child: Column( + children: const [ + Center( + child: Text("Enter Pin"), + ) + ], + )), + ), + ), + ); + + expect(find.byType(Shake), findsOneWidget); + expect(find.byType(Text), findsOneWidget); + }); +} diff --git a/test/widget_tests/trade_card_test.dart b/test/widget_tests/trade_card_test.dart new file mode 100644 index 000000000..b21881781 --- /dev/null +++ b/test/widget_tests/trade_card_test.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/utilities/theme/light_colors.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/trade_card.dart'; + +void main() { + testWidgets("Test Trade card builds", (widgetTester) async { + final trade = Trade( + uuid: "uuid", + tradeId: "trade id", + rateType: "Estimate rate", + direction: "", + timestamp: DateTime.parse("1662544771"), + updatedAt: DateTime.parse("1662544771"), + payInCurrency: "BTC", + payInAmount: "10", + payInAddress: "btc address", + payInNetwork: "", + payInExtraId: "", + payInTxid: "", + payOutCurrency: "xmr", + payOutAmount: "10", + payOutAddress: "xmr address", + payOutNetwork: "", + payOutExtraId: "", + payOutTxid: "", + refundAddress: "refund address", + refundExtraId: "", + status: "Failed", + exchangeName: "Some Exchange"); + + await widgetTester.pumpWidget( + ProviderScope( + overrides: [], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: TradeCard(trade: trade, onTap: () {}), + ), + ), + ); + + expect(find.byType(TradeCard), findsOneWidget); + expect(find.text("BTC → XMR"), findsOneWidget); + expect(find.text("Some Exchange"), findsOneWidget); + }); +} diff --git a/test/widget_tests/transaction_card_test.dart b/test/widget_tests/transaction_card_test.dart index b23254f59..3f46794bd 100644 --- a/test/widget_tests/transaction_card_test.dart +++ b/test/widget_tests/transaction_card_test.dart @@ -131,6 +131,99 @@ void main() { verifyNoMoreInteractions(mockLocaleService); }); + testWidgets("Anonymized confirmed tx displays correctly", (tester) async { + final mockManager = MockManager(); + final mockLocaleService = MockLocaleService(); + final wallets = MockWallets(); + final mockPrefs = MockPrefs(); + final mockPriceService = MockPriceService(); + + final tx = Transaction( + txid: "some txid", + confirmedStatus: true, + timestamp: 1648595998, + txType: "Anonymized", + amount: 100000000, + aliens: [], + worthNow: "0.01", + worthAtBlockTimestamp: "0.01", + fees: 3794, + inputSize: 1, + outputSize: 1, + inputs: [], + outputs: [], + address: "", + height: 450123, + subType: "mint", + confirmations: 10, + isCancelled: false); + + final CoinServiceAPI wallet = MockFiroWallet(); + + when(wallet.coin.ticker).thenAnswer((_) => "FIRO"); + when(mockLocaleService.locale).thenAnswer((_) => "en_US"); + when(mockPrefs.currency).thenAnswer((_) => "USD"); + when(mockPrefs.externalCalls).thenAnswer((_) => true); + when(mockPriceService.getPrice(Coin.firo)) + .thenAnswer((realInvocation) => Tuple2(Decimal.ten, 0.00)); + + when(wallet.coin).thenAnswer((_) => Coin.firo); + + when(wallets.getManager("wallet-id")) + .thenAnswer((realInvocation) => Manager(wallet)); + // + await tester.pumpWidget( + ProviderScope( + overrides: [ + walletsChangeNotifierProvider.overrideWithValue(wallets), + localeServiceChangeNotifierProvider + .overrideWithValue(mockLocaleService), + prefsChangeNotifierProvider.overrideWithValue(mockPrefs), + priceAnd24hChangeNotifierProvider.overrideWithValue(mockPriceService) + ], + child: MaterialApp( + theme: ThemeData( + extensions: [ + StackColors.fromStackColorTheme( + LightColors(), + ), + ], + ), + home: TransactionCard(transaction: tx, walletId: "wallet-id"), + ), + ), + ); + + // + final title = find.text("Anonymized"); + // final price1 = find.text("0.00 USD"); + final amount = find.text("1.00000000 FIRO"); + + final icon = find.byIcon(FeatherIcons.arrowUp); + + expect(title, findsOneWidget); + // expect(price1, findsOneWidget); + expect(amount, findsOneWidget); + // expect(icon, findsOneWidget); + // + await tester.pumpAndSettle(Duration(seconds: 2)); + // + // final price2 = find.text("\$10.00"); + // expect(price2, findsOneWidget); + // + // verify(mockManager.addListener(any)).called(1); + verify(mockLocaleService.addListener(any)).called(1); + + verify(mockPrefs.currency).called(1); + verify(mockPriceService.getPrice(Coin.firo)).called(1); + verify(wallet.coin.ticker).called(1); + + verify(mockLocaleService.locale).called(1); + + verifyNoMoreInteractions(mockManager); + verifyNoMoreInteractions(mockLocaleService); + }); + testWidgets("Received unconfirmed tx displays correctly", (tester) async { final mockManager = MockManager(); final mockLocaleService = MockLocaleService();