Merge branch 'staging' into windows

This commit is contained in:
Josh Babb 2023-06-08 10:12:06 -05:00
commit 1040c68f51
56 changed files with 3485 additions and 2038 deletions

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 01bad9471c4b41392cd2aa98f5655dc14dedc997
Subproject commit c34af5aadd0c986023a1ab998ca6972582142c4c

View file

@ -205,6 +205,27 @@ void main() async {
// check and update or install default themes
await ThemeService.instance.checkDefaultThemesOnStartup();
// verify current user preference theme and revert to default
// if problems are found to prevent app being unusable
if (!(await ThemeService.instance
.verifyInstalled(themeId: Prefs.instance.themeId))) {
Prefs.instance.themeId = "light";
}
// verify current user preference light brightness theme and revert to default
// if problems are found to prevent app being unusable
if (!(await ThemeService.instance
.verifyInstalled(themeId: Prefs.instance.systemBrightnessLightThemeId))) {
Prefs.instance.systemBrightnessLightThemeId = "light";
}
// verify current user preference dark brightness theme and revert to default
// if problems are found to prevent app being unusable
if (!(await ThemeService.instance
.verifyInstalled(themeId: Prefs.instance.systemBrightnessDarkThemeId))) {
Prefs.instance.systemBrightnessDarkThemeId = "dark";
}
runApp(const ProviderScope(child: MyApp()));
}

View file

@ -1939,7 +1939,9 @@ class ThemeAssets implements IThemeAssets {
@override
late final String? background;
// todo: add all assets expected in json
@override
@ignore
String? get walletSummaryCardBackground => null;
ThemeAssets();
@ -2102,6 +2104,8 @@ class ThemeAssetsV2 implements IThemeAssets {
late final String? loadingGif;
@override
late final String? background;
@override
late final String? walletSummaryCardBackground;
late final String coinPlaceholder;
@ -2196,6 +2200,10 @@ class ThemeAssetsV2 implements IThemeAssets {
: null
..background = json["background"] is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["background"] as String}"
: null
..walletSummaryCardBackground = json["walletSummaryCardBackground"]
is String
? "$applicationThemesDirectoryPath/$themeId/assets/${json["walletSummaryCardBackground"] as String}"
: null;
}
@ -2246,6 +2254,7 @@ class ThemeAssetsV2 implements IThemeAssets {
'txExchangeFailed: $txExchangeFailed, '
'loadingGif: $loadingGif, '
'background: $background, '
'walletSummaryCardBackground: $walletSummaryCardBackground, '
'coinPlaceholder: $coinPlaceholder, '
'coinIcons: $coinIcons, '
'coinImages: $coinImages, '
@ -2276,4 +2285,5 @@ abstract class IThemeAssets {
String? get loadingGif;
String? get background;
String? get walletSummaryCardBackground;
}

View file

@ -25895,6 +25895,11 @@ const ThemeAssetsV2Schema = Schema(
id: 23,
name: r'txExchangePending',
type: IsarType.string,
),
r'walletSummaryCardBackground': PropertySchema(
id: 24,
name: r'walletSummaryCardBackground',
type: IsarType.string,
)
},
estimateSize: _themeAssetsV2EstimateSize,
@ -25943,6 +25948,12 @@ int _themeAssetsV2EstimateSize(
bytesCount += 3 + object.txExchange.length * 3;
bytesCount += 3 + object.txExchangeFailed.length * 3;
bytesCount += 3 + object.txExchangePending.length * 3;
{
final value = object.walletSummaryCardBackground;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
return bytesCount;
}
@ -25976,6 +25987,7 @@ void _themeAssetsV2Serialize(
writer.writeString(offsets[21], object.txExchange);
writer.writeString(offsets[22], object.txExchangeFailed);
writer.writeString(offsets[23], object.txExchangePending);
writer.writeString(offsets[24], object.walletSummaryCardBackground);
}
ThemeAssetsV2 _themeAssetsV2Deserialize(
@ -26009,6 +26021,7 @@ ThemeAssetsV2 _themeAssetsV2Deserialize(
object.txExchange = reader.readString(offsets[21]);
object.txExchangeFailed = reader.readString(offsets[22]);
object.txExchangePending = reader.readString(offsets[23]);
object.walletSummaryCardBackground = reader.readStringOrNull(offsets[24]);
return object;
}
@ -26067,6 +26080,8 @@ P _themeAssetsV2DeserializeProp<P>(
return (reader.readString(offset)) as P;
case 23:
return (reader.readString(offset)) as P;
case 24:
return (reader.readStringOrNull(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
@ -29372,6 +29387,162 @@ extension ThemeAssetsV2QueryFilter
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'walletSummaryCardBackground',
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'walletSummaryCardBackground',
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'walletSummaryCardBackground',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundContains(String value,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'walletSummaryCardBackground',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundMatches(String pattern,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'walletSummaryCardBackground',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'walletSummaryCardBackground',
value: '',
));
});
}
QueryBuilder<ThemeAssetsV2, ThemeAssetsV2, QAfterFilterCondition>
walletSummaryCardBackgroundIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'walletSummaryCardBackground',
value: '',
));
});
}
}
extension ThemeAssetsV2QueryObject

View file

@ -978,21 +978,27 @@ class _SendViewState extends ConsumerState<SendView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isPaynymSend ? "Send to PayNym address" : "Send to",
isPaynymSend
? "Send to PayNym address"
: "Send to",
style: STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
if (coin == Coin.monero)
CustomTextButton(
text: "Use OpenAlias",
onTap: () async {
await showModalBottomSheet(context: context, builder: (context) => OpenAliasBottomSheet(
onSelected: (address) {
sendToController.text = address;
},
));
},
)
// if (coin == Coin.monero)
// CustomTextButton(
// text: "Use OpenAlias",
// onTap: () async {
// await showModalBottomSheet(
// context: context,
// builder: (context) =>
// OpenAliasBottomSheet(
// onSelected: (address) {
// sendToController.text = address;
// },
// ),
// );
// },
// ),
],
),
const SizedBox(

View file

@ -1,148 +1,151 @@
/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/constants.dart';
import '../../../utilities/text_styles.dart';
import 'package:basic_utils/basic_utils.dart';
class OpenAliasBottomSheet extends ConsumerStatefulWidget {
const OpenAliasBottomSheet({
Key? key,
required this.onSelected,
}) : super(key: key);
final Null Function(String) onSelected;
@override
ConsumerState<OpenAliasBottomSheet> createState() =>
_OpenAliasBottomSheetState();
}
class _OpenAliasBottomSheetState extends ConsumerState<OpenAliasBottomSheet> {
late TextEditingController textEditingController;
@override
void initState() {
super.initState();
textEditingController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Padding(
padding: const EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: 20,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
width: 60,
height: 4,
),
),
const SizedBox(
height: 36,
),
Text(
'OpenAlias Address',
style: STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
const SizedBox(
height: 8,
),
TextField(
controller: textEditingController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
const SizedBox(
height: 16,
),
Align(
alignment: Alignment.bottomCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 480,
minHeight: 70,
),
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonStyle(context),
onPressed: () async {
String text = textEditingController.text.trim().replaceAll("@", ".");
List<RRecord>? result = await DnsUtils.lookupRecord(text, RRecordType.TXT);
String address = "";
if (result != null && result.isNotEmpty) {
for (RRecord record in result) {
if (record.data.startsWith("oa1:xmr")) {
List<String> datas = record.data.split(" ");
for (String data in datas) {
if (data.startsWith("recipient_address=")) {
address = data.substring("recipient_address=".length).replaceAll(";", "");
break;
}
}
break;
}
}
}
widget.onSelected(address!);
Navigator.of(context).pop();
},
child: Text(
"Enter",
style: STextStyles.button(context),
),
),
),
)
],
),
),
),
),
);
}
}
// /*
// * This file is part of Stack Wallet.
// *
// * Copyright (c) 2023 Cypher Stack
// * All Rights Reserved.
// * The code is distributed under GPLv3 license, see LICENSE file for details.
// * Generated by Cypher Stack on 2023-05-26
// *
// */
//
// import 'package:basic_utils/basic_utils.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_riverpod/flutter_riverpod.dart';
// import 'package:stackwallet/themes/stack_colors.dart';
// import 'package:stackwallet/utilities/constants.dart';
// import 'package:stackwallet/utilities/text_styles.dart';
//
// class OpenAliasBottomSheet extends ConsumerStatefulWidget {
// const OpenAliasBottomSheet({
// Key? key,
// required this.onSelected,
// }) : super(key: key);
//
// final Null Function(String) onSelected;
//
// @override
// ConsumerState<OpenAliasBottomSheet> createState() =>
// _OpenAliasBottomSheetState();
// }
//
// class _OpenAliasBottomSheetState extends ConsumerState<OpenAliasBottomSheet> {
// late TextEditingController textEditingController;
//
// @override
// void initState() {
// super.initState();
// textEditingController = TextEditingController();
// }
//
// @override
// Widget build(BuildContext context) {
// return SingleChildScrollView(
// child: Padding(
// padding:
// EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
// child: Container(
// decoration: BoxDecoration(
// color: Theme.of(context).extension<StackColors>()!.popupBG,
// borderRadius: const BorderRadius.vertical(
// top: Radius.circular(20),
// ),
// ),
// child: Padding(
// padding: const EdgeInsets.only(
// top: 20,
// left: 20,
// right: 20,
// bottom: 20,
// ),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Center(
// child: Container(
// decoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG,
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// width: 60,
// height: 4,
// ),
// ),
// const SizedBox(
// height: 36,
// ),
// Text(
// 'OpenAlias Address',
// style: STextStyles.smallMed12(context),
// textAlign: TextAlign.left,
// ),
// const SizedBox(
// height: 8,
// ),
// TextField(
// controller: textEditingController,
// decoration: const InputDecoration(
// border: OutlineInputBorder(),
// ),
// ),
// const SizedBox(
// height: 16,
// ),
// Align(
// alignment: Alignment.bottomCenter,
// child: ConstrainedBox(
// constraints: const BoxConstraints(
// minWidth: 480,
// minHeight: 70,
// ),
// child: TextButton(
// style: Theme.of(context)
// .extension<StackColors>()!
// .getPrimaryEnabledButtonStyle(context),
// onPressed: () async {
// String text = textEditingController.text
// .trim()
// .replaceAll("@", ".");
// List<RRecord>? result =
// await DnsUtils.lookupRecord(text, RRecordType.TXT);
// String address = "";
// if (result != null && result.isNotEmpty) {
// for (RRecord record in result) {
// if (record.data.startsWith("oa1:xmr")) {
// List<String> datas = record.data.split(" ");
// for (String data in datas) {
// if (data.startsWith("recipient_address=")) {
// address = data
// .substring("recipient_address=".length)
// .replaceAll(";", "");
// break;
// }
// }
// break;
// }
// }
// }
// widget.onSelected(address);
// if (mounted) {
// Navigator.of(context).pop();
// }
// },
// child: Text(
// "Enter",
// style: STextStyles.button(context),
// ),
// ),
// ),
// )
// ],
// ),
// ),
// ),
// ),
// );
// }
// }

View file

@ -46,6 +46,7 @@ class _ChooseUnitSheetState extends ConsumerState<ChooseUnitSheet> {
top: 10,
bottom: 0,
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@ -71,7 +72,7 @@ class _ChooseUnitSheetState extends ConsumerState<ChooseUnitSheet> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Phrase length",
"Coin units",
style: STextStyles.pageTitleH2(context),
textAlign: TextAlign.left,
),
@ -137,6 +138,7 @@ class _ChooseUnitSheetState extends ConsumerState<ChooseUnitSheet> {
],
),
),
),
);
}
}

View file

@ -66,26 +66,38 @@ class _RefreshButtonState extends ConsumerState<WalletRefreshButton> {
widget.tokenContractAddress == null) {
switch (event.newStatus) {
case WalletSyncStatus.unableToSync:
if (_spinController.hasLoadedAnimation) {
_spinController.stop?.call();
}
break;
case WalletSyncStatus.synced:
if (_spinController.hasLoadedAnimation) {
_spinController.stop?.call();
}
break;
case WalletSyncStatus.syncing:
if (_spinController.hasLoadedAnimation) {
_spinController.repeat?.call();
}
break;
}
} else if (widget.tokenContractAddress != null &&
event.walletId == widget.walletId + widget.tokenContractAddress!) {
switch (event.newStatus) {
case WalletSyncStatus.unableToSync:
if (_spinController.hasLoadedAnimation) {
_spinController.stop?.call();
}
break;
case WalletSyncStatus.synced:
if (_spinController.hasLoadedAnimation) {
_spinController.stop?.call();
}
break;
case WalletSyncStatus.syncing:
if (_spinController.hasLoadedAnimation) {
_spinController.repeat?.call();
}
break;
}
}

View file

@ -174,7 +174,7 @@ class BananoWallet extends CoinServiceAPI
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
try {
// our address:
final String publicAddress = await getAddressFromMnemonic();
final String publicAddress = await currentReceivingAddress;
// first get the account balance:
final balanceBody = jsonEncode({
@ -276,8 +276,18 @@ class BananoWallet extends CoinServiceAPI
}
}
Future<Address?> get _currentReceivingAddress => db
.getAddresses(walletId)
.filter()
.typeEqualTo(AddressType.banano)
.and()
.subTypeEqualTo(AddressSubType.receiving)
.sortByDerivationIndexDesc()
.findFirst();
@override
Future<String> get currentReceivingAddress => getAddressFromMnemonic();
Future<String> get currentReceivingAddress async =>
(await _currentReceivingAddress)?.value ?? await getAddressFromMnemonic();
@override
Future<Amount> estimateFeeFor(Amount amount, int feeRate) {
@ -298,7 +308,7 @@ class BananoWallet extends CoinServiceAPI
Future<void> updateBalance() async {
final body = jsonEncode({
"action": "account_balance",
"account": await getAddressFromMnemonic(),
"account": await currentReceivingAddress,
});
final headers = {
"Content-Type": "application/json",
@ -333,7 +343,7 @@ class BananoWallet extends CoinServiceAPI
};
// our address:
final String publicAddress = await getAddressFromMnemonic();
final String publicAddress = await currentReceivingAddress;
// first check if the account is open:
// get the account info (we need the frontier and representative):
@ -452,7 +462,7 @@ class BananoWallet extends CoinServiceAPI
body: jsonEncode({
"action": "receivable",
"source": "true",
"account": await getAddressFromMnemonic(),
"account": await currentReceivingAddress,
"count": "-1",
}));
@ -474,7 +484,8 @@ class BananoWallet extends CoinServiceAPI
Future<void> updateTransactions() async {
await confirmAllReceivable();
final String publicAddress = await getAddressFromMnemonic();
final receivingAddress = (await _currentReceivingAddress)!;
final String publicAddress = receivingAddress.value;
final response = await http.post(Uri.parse(getCurrentNode().host),
headers: {"Content-Type": "application/json"},
body: jsonEncode({
@ -483,7 +494,8 @@ class BananoWallet extends CoinServiceAPI
"count": "-1",
}));
final data = await jsonDecode(response.body);
final transactions = data["history"] as List<dynamic>;
final transactions =
data["history"] is List ? data["history"] as List<dynamic> : [];
if (transactions.isEmpty) {
return;
} else {
@ -521,20 +533,16 @@ class BananoWallet extends CoinServiceAPI
numberOfMessages: null,
);
Address address = Address(
Address address = transactionType == TransactionType.incoming
? receivingAddress
: Address(
walletId: walletId,
publicKey: [],
value: transactionType == TransactionType.incoming
? publicAddress
: tx["account"].toString(),
value: tx["account"].toString(),
derivationIndex: 0,
derivationPath: null,
type: transactionType == TransactionType.incoming
? AddressType.nonWallet
: AddressType.banano,
subType: transactionType == TransactionType.incoming
? AddressSubType.receiving
: AddressSubType.nonWallet,
type: AddressType.banano,
subType: AddressSubType.nonWallet,
);
Tuple2<Transaction, Address> tuple = Tuple2(transaction, address);
transactionList.add(tuple);
@ -599,7 +607,7 @@ class BananoWallet extends CoinServiceAPI
publicKey: [], // TODO: add public key
derivationIndex: 0,
derivationPath: null,
type: AddressType.unknown,
type: AddressType.banano,
subType: AddressSubType.receiving,
);
@ -694,10 +702,10 @@ class BananoWallet extends CoinServiceAPI
final address = Address(
walletId: walletId,
value: publicAddress,
publicKey: [], // TODO: add public key
publicKey: [],
derivationIndex: 0,
derivationPath: null,
type: AddressType.unknown,
type: AddressType.banano,
subType: AddressSubType.receiving,
);
@ -734,7 +742,11 @@ class BananoWallet extends CoinServiceAPI
coin,
),
);
} catch (e) {
} catch (e, s) {
Logging.instance.log(
"Failed to refresh banano wallet \'$walletName\': $e\n$s",
level: LogLevel.Warning,
);
GlobalEventBus.instance.fire(
WalletSyncStatusChangedEvent(
WalletSyncStatus.unableToSync,
@ -756,15 +768,20 @@ class BananoWallet extends CoinServiceAPI
}
@override
Future<bool> testNetworkConnection() {
http
.get(Uri.parse("${getCurrentNode().host}?action=version"))
.then((response) {
if (response.statusCode == 200) {
return true;
}
});
return Future.value(false);
Future<bool> testNetworkConnection() async {
final uri = Uri.parse(getCurrentNode().host);
final response = await http.post(
uri,
headers: {"Content-Type": "application/json"},
body: jsonEncode(
{
"action": "version",
},
),
);
return response.statusCode == 200;
}
Timer? _networkAliveTimer;
@ -841,7 +858,7 @@ class BananoWallet extends CoinServiceAPI
}
Future<void> updateChainHeight() async {
final String publicAddress = await getAddressFromMnemonic();
final String publicAddress = await currentReceivingAddress;
// first get the account balance:
final infoBody = jsonEncode({
"action": "account_info",
@ -857,7 +874,9 @@ class BananoWallet extends CoinServiceAPI
);
final infoData = jsonDecode(infoResponse.body);
final int height = int.parse(infoData["confirmation_height"].toString());
await updateCachedChainHeight(height);
final int? height = int.tryParse(
infoData["confirmation_height"].toString(),
);
await updateCachedChainHeight(height ?? 0);
}
}

View file

@ -504,7 +504,8 @@ class NanoWallet extends CoinServiceAPI
"count": "-1",
}));
final data = await jsonDecode(response.body);
final transactions = data["history"] as List<dynamic>;
final transactions =
data["history"] is List ? data["history"] as List<dynamic> : [];
if (transactions.isEmpty) {
return;
} else {
@ -775,15 +776,20 @@ class NanoWallet extends CoinServiceAPI
}
@override
Future<bool> testNetworkConnection() {
http
.get(Uri.parse("${getCurrentNode().host}?action=version"))
.then((response) {
if (response.statusCode == 200) {
return true;
}
});
return Future.value(false);
Future<bool> testNetworkConnection() async {
final uri = Uri.parse(getCurrentNode().host);
final response = await http.post(
uri,
headers: {"Content-Type": "application/json"},
body: jsonEncode(
{
"action": "version",
},
),
);
return response.statusCode == 200;
}
Timer? _networkAliveTimer;
@ -876,7 +882,9 @@ class NanoWallet extends CoinServiceAPI
);
final infoData = jsonDecode(infoResponse.body);
final int height = int.parse(infoData["confirmation_height"].toString());
await updateCachedChainHeight(height);
final int? height = int.tryParse(
infoData["confirmation_height"].toString(),
);
await updateCachedChainHeight(height ?? 0);
}
}

View file

@ -58,6 +58,12 @@ class ThemeService {
applicationThemesDirectoryPath: themesDir.path,
);
try {
theme.assets;
} catch (_) {
throw Exception("Invalid theme: Failed to create assets object");
}
final String assetsPath = "${themesDir.path}/${theme.themeId}";
for (final file in archive.files) {
@ -167,9 +173,15 @@ class ThemeService {
// TODO more thorough check/verification of theme
Future<bool> verifyInstalled({required String themeId}) async {
final dbHasTheme =
await db.isar.stackThemes.where().themeIdEqualTo(themeId).count() > 0;
if (dbHasTheme) {
final theme =
await db.isar.stackThemes.where().themeIdEqualTo(themeId).findFirst();
if (theme != null) {
try {
theme.assets;
} catch (_) {
return false;
}
final themesDir = await StackFileSystem.applicationThemesDirectory();
final jsonFileExists =
await File("${themesDir.path}/$themeId/theme.json").exists();

View file

@ -81,7 +81,8 @@ extension AmountUnitExt on AmountUnit {
return "gwei";
} else if (coin == Coin.wownero ||
coin == Coin.monero ||
coin == Coin.nano) {
coin == Coin.nano ||
coin == Coin.banano) {
return "n${coin.ticker}";
} else {
return "sats";
@ -91,7 +92,8 @@ extension AmountUnitExt on AmountUnit {
return "mwei";
} else if (coin == Coin.wownero ||
coin == Coin.monero ||
coin == Coin.nano) {
coin == Coin.nano ||
coin == Coin.banano) {
return "p${coin.ticker}";
} else {
return "invalid";
@ -99,7 +101,7 @@ extension AmountUnitExt on AmountUnit {
case AmountUnit.femto:
if (coin == Coin.ethereum) {
return "kwei";
} else if (coin == Coin.nano) {
} else if (coin == Coin.nano || coin == Coin.banano) {
return "f${coin.ticker}";
} else {
return "invalid";
@ -107,31 +109,31 @@ extension AmountUnitExt on AmountUnit {
case AmountUnit.atto:
if (coin == Coin.ethereum) {
return "wei";
} else if (coin == Coin.nano) {
} else if (coin == Coin.nano || coin == Coin.banano) {
return "a${coin.ticker}";
} else {
return "invalid";
}
case AmountUnit.zepto:
if (coin == Coin.nano) {
if (coin == Coin.nano || coin == Coin.banano) {
return "z${coin.ticker}";
} else {
return "invalid";
}
case AmountUnit.yocto:
if (coin == Coin.nano) {
if (coin == Coin.nano || coin == Coin.banano) {
return "y${coin.ticker}";
} else {
return "invalid";
}
case AmountUnit.ronto:
if (coin == Coin.nano) {
if (coin == Coin.nano || coin == Coin.banano) {
return "r${coin.ticker}";
} else {
return "invalid";
}
case AmountUnit.quecto:
if (coin == Coin.nano) {
if (coin == Coin.nano || coin == Coin.banano) {
return "q${coin.ticker}";
} else {
return "invalid";

View file

@ -31,6 +31,7 @@ import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow;
import 'package:stackwallet/utilities/constants.dart';
enum Coin {
banano,
bitcoin,
bitcoincash,
dogecoin,
@ -41,10 +42,9 @@ enum Coin {
litecoin,
monero,
namecoin,
nano,
particl,
wownero,
nano,
banano,
///
@ -52,10 +52,10 @@ enum Coin {
///
bitcoinTestNet,
litecoinTestNet,
bitcoincashTestnet,
dogecoinTestNet,
firoTestNet,
litecoinTestNet,
}
final int kTestNetCoinCount = 4; // Util.isDesktop ? 5 : 4;

View file

@ -8,8 +8,6 @@
*
*/
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/utilities/amount/amount_unit.dart';
@ -866,8 +864,8 @@ class Prefs extends ChangeNotifier {
boxName: DB.boxNamePrefs,
key: "maxDecimalsFor${coin.name}",
) as int? ??
max(coin.decimals,
18); // use some sane max rather than up to 30 that nano uses
(coin.decimals > 18 ? 18 : coin.decimals);
// use some sane max rather than up to 30 that nano uses
_amountDecimals[coin] = decimals;
}
}

View file

@ -17,6 +17,8 @@ class RotatingArrowsController {
VoidCallback? forward;
VoidCallback? repeat;
VoidCallback? stop;
bool hasLoadedAnimation = false;
}
class RotatingArrows extends StatefulWidget {
@ -86,6 +88,7 @@ class _RotatingArrowsState extends State<RotatingArrows>
),
onLoaded: (composition) {
animationController.duration = composition.duration;
widget.controller?.hasLoadedAnimation = true;
// if controller was not set just assume continuous repeat
if (widget.spinByDefault) {

View file

@ -17,14 +17,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.13.0"
animations:
dependency: "direct main"
description:
name: animations
sha256: fe8a6bdca435f718bb1dc8a11661b2c22504c6da40ef934cee8327ed77934164
url: "https://pub.dev"
source: hosted
version: "2.0.7"
another_flushbar:
dependency: "direct main"
description:
@ -33,14 +25,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.12.30"
app_settings:
dependency: "direct main"
description:
name: app_settings
sha256: "66715a323ac36d6c8201035ba678777c0d2ea869e4d7064300d95af10c3bb8cb"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
archive:
dependency: "direct main"
description:
@ -93,8 +77,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "9e3927a53a606cc3f87f98c5cba36e9fd59917a5"
resolved-ref: "9e3927a53a606cc3f87f98c5cba36e9fd59917a5"
ref: b6d2a5b4cd17311d917787c0f9505f04932659b1
resolved-ref: b6d2a5b4cd17311d917787c0f9505f04932659b1
url: "https://github.com/cypherstack/bech32.git"
source: git
version: "0.2.1"
@ -110,8 +94,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "787814bfea1af30ecf9d90c2c416846def1801f1"
resolved-ref: "787814bfea1af30ecf9d90c2c416846def1801f1"
ref: "0cd6d54e2860bea68fc50c801cb9db2a760192fb"
resolved-ref: "0cd6d54e2860bea68fc50c801cb9db2a760192fb"
url: "https://github.com/cypherstack/stack-bip39.git"
source: git
version: "1.0.6"
@ -119,8 +103,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: f0e50250f84438a11364018f8ed3dd5c09ab2e7b
resolved-ref: f0e50250f84438a11364018f8ed3dd5c09ab2e7b
ref: "081ca1863c2feba00c35bb5b297902f12f499941"
resolved-ref: "081ca1863c2feba00c35bb5b297902f12f499941"
url: "https://github.com/cypherstack/bip47.git"
source: git
version: "2.0.0"
@ -137,8 +121,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "6136230e660fd1f681d18c75d38ca7de4d81187c"
resolved-ref: "6136230e660fd1f681d18c75d38ca7de4d81187c"
ref: af6d6c27edfe2e7cc35772ed2684eb4cc826f0e4
resolved-ref: af6d6c27edfe2e7cc35772ed2684eb4cc826f0e4
url: "https://github.com/cypherstack/bitcoindart.git"
source: git
version: "3.0.1"
@ -950,14 +934,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
jsonrpc2:
dependency: "direct main"
description:
name: jsonrpc2
sha256: "98a71b834240ca6d003499ab8f28d1c35aa8ca90235b51e972e0f70596b86bd3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
keyboard_dismisser:
dependency: "direct main"
description:
@ -1341,22 +1317,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.3"
rpc_dispatcher:
dependency: transitive
description:
name: rpc_dispatcher
sha256: b6ddcae58b3fbc1172a7c2dc8ab30d2f1090db8c7a728e4405bd10142dc48a47
url: "https://pub.dev"
source: hosted
version: "1.0.1"
rpc_exceptions:
dependency: transitive
description:
name: rpc_exceptions
sha256: "09b2e5f3f805b65a262b40e3410d79fb916ff5be2a65e2f6b8b0eeef7aa965b7"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
rxdart:
dependency: "direct main"
description:
@ -1571,14 +1531,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
string_to_hex:
dependency: "direct main"
description:
name: string_to_hex
sha256: "63e5dc1f4821a2449d505033fbd4569f7020ebf30ddffb54d00ebaba8e144a49"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
string_validator:
dependency: "direct main"
description:
@ -1807,7 +1759,7 @@ packages:
dependency: "direct overridden"
description:
path: wakelock_windows
ref: win32-v5
ref: "2a9bca63a540771f241d688562351482b2cf234c"
resolved-ref: "2a9bca63a540771f241d688562351482b2cf234c"
url: "https://github.com/timsneath/wakelock"
source: git

View file

@ -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.7.12+177
version: 1.7.13+178
environment:
sdk: ">=3.0.2 <4.0.0"
@ -42,16 +42,10 @@ dependencies:
flutter_libepiccash:
path: ./crypto_plugins/flutter_libepiccash
# firo_flutter:
# git:
# url: https://github.com/cypherstack/firo_flutter.git
# ref: update
# path: ./crypto_plugins/firo_flutter
bitcoindart:
git:
url: https://github.com/cypherstack/bitcoindart.git
ref: 6136230e660fd1f681d18c75d38ca7de4d81187c
ref: af6d6c27edfe2e7cc35772ed2684eb4cc826f0e4
stack_wallet_backup:
git:
@ -61,7 +55,7 @@ dependencies:
bip47:
git:
url: https://github.com/cypherstack/bip47.git
ref: f0e50250f84438a11364018f8ed3dd5c09ab2e7b
ref: 081ca1863c2feba00c35bb5b297902f12f499941
# Utility plugins
http: ^0.13.0
@ -69,7 +63,6 @@ dependencies:
permission_handler: ^10.0.0
flutter_local_notifications: ^9.4.0
rxdart: ^0.27.3
# filesystem_picker: ^3.0.0-beta.1
zxcvbn: ^1.0.0
dart_numerics: ^0.0.6
@ -82,7 +75,7 @@ dependencies:
bip39:
git:
url: https://github.com/cypherstack/stack-bip39.git
ref: 787814bfea1af30ecf9d90c2c416846def1801f1
ref: 0cd6d54e2860bea68fc50c801cb9db2a760192fb
bitbox:
git:
url: https://github.com/PiRK/bitbox-flutter.git
@ -91,12 +84,11 @@ dependencies:
bech32:
git:
url: https://github.com/cypherstack/bech32.git
ref: 9e3927a53a606cc3f87f98c5cba36e9fd59917a5
ref: b6d2a5b4cd17311d917787c0f9505f04932659b1
bs58check: ^1.0.2
# Eth Plugins
web3dart: 2.6.1
string_to_hex: 0.2.2
ethereum_addresses: 1.0.2
# Storage plugins
@ -107,7 +99,6 @@ dependencies:
# UI/Component plugins
flutter_native_splash: ^2.2.4
animations: ^2.0.2
google_fonts: ^4.0.4
url_launcher: ^6.0.5
flutter_svg: ^1.0.1
@ -117,9 +108,7 @@ dependencies:
uuid: ^3.0.5
flutter_rounded_date_picker: ^3.0.1
crypto: ^3.0.2
jsonrpc2: ^3.0.1
barcode_scan2: ^4.2.0
app_settings: ^4.1.1
wakelock: ^0.6.2
intl: ^0.17.0
devicelocale: ^0.6.0
@ -134,11 +123,8 @@ dependencies:
pointycastle: ^3.6.0
package_info_plus: ^4.0.2
lottie: ^2.3.2
# shared_preferences: ^2.0.15
file_picker: ^5.3.1
# connectivity_plus: 2.3.6+1
connectivity_plus: ^4.0.1
# document_file_save_plus: ^1.0.5
isar: 3.0.5
isar_flutter_libs: 3.0.5 # contains the binaries
dropdown_button2: ^2.1.3
@ -192,25 +178,27 @@ flutter_native_splash:
android_disable_fullscreen: true
dependency_overrides:
# required for dart 3, at least until a fix is merged upstream
wakelock_windows:
git:
url: https://github.com/timsneath/wakelock
ref: win32-v5
ref: 2a9bca63a540771f241d688562351482b2cf234c
path: wakelock_windows
# required for libmonero - can remove once libmonero has been updated, PR has been merged in swb
stack_wallet_backup:
git:
url: https://github.com/cypherstack/stack_wallet_backup.git
ref: ee1da8a9ba1cbeb50c5b354ea1fd5a25b7c5a3ed
bech32:
git:
url: https://github.com/cypherstack/bech32.git
ref: 9e3927a53a606cc3f87f98c5cba36e9fd59917a5
# required override for nanodart
bip39:
git:
url: https://github.com/cypherstack/stack-bip39.git
ref: 787814bfea1af30ecf9d90c2c416846def1801f1
crypto: 3.0.2
flutter_secure_storage: ^8.0.0
ref: 0cd6d54e2860bea68fc50c801cb9db2a760192fb
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -16,6 +16,7 @@ import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/amount/amount_unit.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/prefs.dart';
@ -35,155 +36,161 @@ import 'send_view_test.mocks.dart';
MockSpec<CoinServiceAPI>(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 mockThemeService = MockThemeService();
// 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(mockThemeService.getTheme(themeId: "light")).thenAnswer(
// (_) => StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// );
// when(mockPrefs.currency).thenAnswer((_) => "USD");
// when(mockPrefs.enableCoinControl).thenAnswer((_) => false);
// 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),
// pThemeService.overrideWithValue(mockThemeService),
// // previewTxButtonStateProvider
// ],
// child: MaterialApp(
// theme: ThemeData(
// extensions: [
// StackColors.fromStackColorTheme(
// StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// ),
// ],
// ),
// home: SendView(
// walletId: "wallet id",
// coin: Coin.bitcoin,
// autoFillData: SendViewAutoFillData(
// address: "send to address", contactLabel: "contact label"),
// ),
// ),
// ),
// );
//
// await widgetTester.pumpAndSettle();
//
// 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 valid address", (widgetTester) async {
final mockWallets = MockWallets();
final mockWalletsService = MockWalletsService();
final mockNodeService = MockNodeService();
final CoinServiceAPI wallet = MockBitcoinWallet();
final mockLocaleService = MockLocaleService();
final mockThemeService = MockThemeService();
final mockPrefs = MockPrefs();
// 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();
// final mockThemeService = MockThemeService();
//
// 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(mockPrefs.enableCoinControl).thenAnswer((_) => false);
// when(wallet.validateAddress("send to address"))
// .thenAnswer((realInvocation) => false);
// when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
// (_) => StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// );
//
// // 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),
// pThemeService.overrideWithValue(mockThemeService)
// // previewTxButtonStateProvider
// ],
// child: MaterialApp(
// theme: ThemeData(
// extensions: [
// StackColors.fromStackColorTheme(
// StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// ),
// ],
// ),
// home: SendView(
// walletId: "wallet id",
// coin: Coin.bitcoin,
// autoFillData: SendViewAutoFillData(
// address: "send to address", contactLabel: "contact label"),
// ),
// ),
// ),
// );
//
// await widgetTester.pumpAndSettle();
//
// 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);
// });
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(mockThemeService.getTheme(themeId: "light")).thenAnswer(
(_) => StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
);
when(mockPrefs.currency).thenAnswer((_) => "USD");
when(mockPrefs.enableCoinControl).thenAnswer((_) => false);
when(mockPrefs.amountUnit(Coin.bitcoin)).thenAnswer(
(_) => AmountUnit.normal,
);
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),
pThemeService.overrideWithValue(mockThemeService),
// previewTxButtonStateProvider
],
child: MaterialApp(
theme: ThemeData(
extensions: [
StackColors.fromStackColorTheme(
StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
),
],
),
home: SendView(
walletId: "wallet id",
coin: Coin.bitcoin,
autoFillData: SendViewAutoFillData(
address: "send to address", contactLabel: "contact label"),
),
),
),
);
await widgetTester.pumpAndSettle();
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();
final mockThemeService = MockThemeService();
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(mockPrefs.enableCoinControl).thenAnswer((_) => false);
when(mockPrefs.amountUnit(Coin.bitcoin)).thenAnswer(
(_) => AmountUnit.normal,
);
when(wallet.validateAddress("send to address"))
.thenAnswer((realInvocation) => false);
when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
(_) => StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
);
// 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),
pThemeService.overrideWithValue(mockThemeService)
// previewTxButtonStateProvider
],
child: MaterialApp(
theme: ThemeData(
extensions: [
StackColors.fromStackColorTheme(
StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
),
],
),
home: SendView(
walletId: "wallet id",
coin: Coin.bitcoin,
autoFillData: SendViewAutoFillData(
address: "send to address", contactLabel: "contact label"),
),
),
),
);
await widgetTester.pumpAndSettle();
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);
});
}

View file

@ -987,7 +987,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i27.BitcoinWallet {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
_i23.Future<List<_i18.UTXO>> get utxos => (super.noSuchMethod(
@ -2837,7 +2837,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(
@ -3205,7 +3205,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -21,328 +21,383 @@ void main() {
boxName: DB.boxNamePrefs, key: "externalCalls", value: true);
});
// test("getPricesAnd24hChange fetch", () async {
// final client = MockClient();
//
// when(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
// "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash"
// ",namecoin,wownero,ethereum,particl&order=market_cap_desc&per_page=50"
// "&page=1&sparkline=false"),
// headers: {
// 'Content-Type': 'application/json'
// })).thenAnswer((_) async => Response(
// '[{"id":"bitcoin","symbol":"btc","name":"Bitcoin","image":"https://asse'
// 'ts.coingecko.com/coins/images/1/large/bitcoin.png?1547033579","curr'
// 'ent_price":1.0,"market_cap":19128800,"market_cap_rank":1,"fully_dil'
// 'uted_valuation":21000000,"total_volume":1272132,"high_24h":1.0,"low'
// '_24h":1.0,"price_change_24h":0.0,"price_change_percentage_24h":0.0,'
// '"market_cap_change_24h":950.0,"market_cap_change_percentage_24h":0.0'
// '0497,"circulating_supply":19128800.0,"total_supply":21000000.0,"max'
// '_supply":21000000.0,"ath":1.003301,"ath_change_percentage":-0.32896'
// ',"ath_date":"2019-10-15T16:00:56.136Z","atl":0.99895134,"atl_change_'
// 'percentage":0.10498,"atl_date":"2019-10-21T00:00:00.000Z","roi":nul'
// 'l,"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin","symb'
// 'ol":"doge","name":"Dogecoin","image":"https://assets.coingecko.com/'
// 'coins/images/5/large/dogecoin.png?1547792256","current_price":3.15e'
// '-06,"market_cap":417916,"market_cap_rank":10,"fully_diluted_valuati'
// 'on":null,"total_volume":27498,"high_24h":3.26e-06,"low_24h":3.13e-0'
// '6,"price_change_24h":-8.6889947714e-08,"price_change_percentage_24h'
// '":-2.68533,"market_cap_change_24h":-11370.894861206936,"market_cap_c'
// 'hange_percentage_24h":-2.64879,"circulating_supply":132670764299.89'
// '4,"total_supply":null,"max_supply":null,"ath":1.264e-05,"ath_change'
// '_percentage":-75.05046,"ath_date":"2021-05-07T23:04:53.026Z","atl":'
// '1.50936e-07,"atl_change_percentage":1989.69346,"atl_date":"2020-12-'
// '17T09:18:05.654Z","roi":null,"last_updated":"2022-08-22T16:38:15.11'
// '3Z"},{"id":"monero","symbol":"xmr","name":"Monero","image":"https:/'
// '/assets.coingecko.com/coins/images/69/large/monero_logo.png?1547033'
// '729","current_price":0.00717236,"market_cap":130002,"market_cap_ran'
// 'k":29,"fully_diluted_valuation":null,"total_volume":4901,"high_24h":'
// '0.00731999,"low_24h":0.00707511,"price_change_24h":-5.6133543212467'
// 'e-05,"price_change_percentage_24h":-0.77656,"market_cap_change_24h"'
// ':-1007.8447677436197,"market_cap_change_percentage_24h":-0.76929,"c'
// 'irculating_supply":18147820.3764146,"total_supply":null,"max_supply'
// '":null,"ath":0.03475393,"ath_change_percentage":-79.32037,"ath_date'
// '":"2018-01-09T00:00:00.000Z","atl":0.00101492,"atl_change_percentag'
// 'e":608.13327,"atl_date":"2014-12-18T00:00:00.000Z","roi":null,"las'
// 't_updated":"2022-08-22T16:38:26.347Z"},{"id":"zcoin","symbol":"firo'
// '","name":"Firo","image":"https://assets.coingecko.com/coins/images/'
// '479/large/firocoingecko.png?1636537544","current_price":0.0001096,"'
// 'market_cap":1252,"market_cap_rank":604,"fully_diluted_valuation":234'
// '9,"total_volume":90.573,"high_24h":0.00011148,"low_24h":0.00010834,'
// '"price_change_24h":-9.87561775002e-07,"price_change_percentage_24h'
// '":-0.89304,"market_cap_change_24h":-10.046635178462793,"market_cap_'
// 'change_percentage_24h":-0.79578,"circulating_supply":11411043.83546'
// '97,"total_supply":21400000.0,"max_supply":21400000.0,"ath":0.016162'
// '72,"ath_change_percentage":-99.3208,"ath_date":"2018-04-04T16:04:48.'
// '408Z","atl":4.268e-05,"atl_change_percentage":157.22799,"atl_date":"'
// '2022-05-12T07:28:47.088Z","roi":null,"last_updated":"2022-08-22T16'
// ':38:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"Epic Cash",'
// '"image":"https://assets.coingecko.com/coins/images/9520/large/Epic_C'
// 'oin_NO_drop_shadow.png?1620122642","current_price":2.803e-05,"marke'
// 't_cap":415.109,"market_cap_rank":953,"fully_diluted_valuation":null'
// ',"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h":2.581e-05'
// ',"price_change_24h":1.9e-06,"price_change_percentage_24h":7.27524,"'
// 'market_cap_change_24h":28.26753,"market_cap_change_percentage_24h":'
// '7.30726,"circulating_supply":14808052.0,"total_supply":21000000.0,"'
// 'max_supply":null,"ath":0.00013848,"ath_change_percentage":-79.75864'
// ',"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74028e-07,"atl_chang'
// 'e_percentage":4783.08078,"atl_date":"2020-03-13T16:55:01.177Z","roi'
// '":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
// 200));
//
// final priceAPI = PriceAPI(client);
// priceAPI.resetLastCalledToForceNextCallToUpdateCache();
//
// final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
//
// expect(
// price.toString(),
// '{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], '
// 'Coin.dogecoin: [0.00000315, -2.68533], '
// 'Coin.eCash: [0, 0.0], Coin.epicCash: [0.00002803, 7.27524], '
// 'Coin.ethereum: [0, 0.0], '
// 'Coin.firo: [0.0001096, -0.89304], Coin.litecoin: [0, 0.0], '
// 'Coin.monero: [0.00717236, -0.77656], Coin.namecoin: [0, 0.0], '
// 'Coin.particl: [0, 0.0], Coin.wownero: [0, 0.0], '
// 'Coin.bitcoinTestNet: [0, 0.0],'
// ' Coin.litecoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], '
// 'Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}',
// );
// verify(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
// "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
// "bitcoin-cash,namecoin,wownero,ethereum,particl"
// "&order=market_cap_desc&per_page=50&page=1&sparkline=false",
// ),
// headers: {'Content-Type': 'application/json'})).called(1);
//
// verifyNoMoreInteractions(client);
// });
test("getPricesAnd24hChange fetch", () async {
final client = MockClient();
// test("cached price fetch", () async {
// final client = MockClient();
//
// when(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&"
// "ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
// "bitcoin-cash,namecoin,wownero,ethereum,particl"
// "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
// headers: {
// 'Content-Type': 'application/json'
// })).thenAnswer((_) async => Response(
// '[{"id":"bitcoin","symbol":"btc","name":"Bitcoin","image":"https://a'
// 'ssets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579","c'
// 'urrent_price":1.0,"market_cap":19128800,"market_cap_rank":1,"fully_'
// 'diluted_valuation":21000000,"total_volume":1272132,"high_24h":1.0,"'
// 'low_24h":1.0,"price_change_24h":0.0,"price_change_percentage_24h":0'
// '.0,"market_cap_change_24h":950.0,"market_cap_change_percentage_24h"'
// ':0.00497,"circulating_supply":19128800.0,"total_supply":21000000.0,"'
// 'max_supply":21000000.0,"ath":1.003301,"ath_change_percentage":-0.32'
// '896,"ath_date":"2019-10-15T16:00:56.136Z","atl":0.99895134,"atl_cha'
// 'nge_percentage":0.10498,"atl_date":"2019-10-21T00:00:00.000Z","roi"'
// ':null,"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin","'
// 'symbol":"doge","name":"Dogecoin","image":"https://assets.coingecko.'
// 'com/coins/images/5/large/dogecoin.png?1547792256","current_price":3'
// '.15e-06,"market_cap":417916,"market_cap_rank":10,"fully_diluted_val'
// 'uation":null,"total_volume":27498,"high_24h":3.26e-06,"low_24h":3.1'
// '3e-06,"price_change_24h":-8.6889947714e-08,"price_change_percentage'
// '_24h":-2.68533,"market_cap_change_24h":-11370.894861206936,"market_'
// 'cap_change_percentage_24h":-2.64879,"circulating_supply":1326707642'
// '99.894,"total_supply":null,"max_supply":null,"ath":1.264e-05,"ath_c'
// 'hange_percentage":-75.05046,"ath_date":"2021-05-07T23:04:53.026Z","'
// 'atl":1.50936e-07,"atl_change_percentage":1989.69346,"atl_date":"202'
// '0-12-17T09:18:05.654Z","roi":null,"last_updated":"2022-08-22T16:38:'
// '15.113Z"},{"id":"monero","symbol":"xmr","name":"Monero","image":"ht'
// 'tps://assets.coingecko.com/coins/images/69/large/monero_logo.png?15'
// '47033729","current_price":0.00717236,"market_cap":130002,"market_cap'
// '_rank":29,"fully_diluted_valuation":null,"total_volume":4901,"high'
// '_24h":0.00731999,"low_24h":0.00707511,"price_change_24h":-5.613354'
// '3212467e-05,"price_change_percentage_24h":-0.77656,"market_cap_chan'
// 'ge_24h":-1007.8447677436197,"market_cap_change_percentage_24h":-0.7'
// '6929,"circulating_supply":18147820.3764146,"total_supply":null,"ma'
// 'x_supply":null,"ath":0.03475393,"ath_change_percentage":-79.32037,"'
// 'ath_date":"2018-01-09T00:00:00.000Z","atl":0.00101492,"atl_change_'
// 'percentage":608.13327,"atl_date":"2014-12-18T00:00:00.000Z","roi":n'
// 'ull,"last_updated":"2022-08-22T16:38:26.347Z"},{"id":"zcoin","symbo'
// 'l":"firo","name":"Firo","image":"https://assets.coingecko.com/coins'
// '/images/479/large/firocoingecko.png?1636537544","current_price":0.0'
// '001096,"market_cap":1252,"market_cap_rank":604,"fully_diluted_valu'
// 'ation":2349,"total_volume":90.573,"high_24h":0.00011148,"low_24h":0'
// '.00010834,"price_change_24h":-9.87561775002e-07,"price_change_perce'
// 'ntage_24h":-0.89304,"market_cap_change_24h":-10.046635178462793,"ma'
// 'rket_cap_change_percentage_24h":-0.79578,"circulating_supply":11411'
// '043.8354697,"total_supply":21400000.0,"max_supply":21400000.0,"ath"'
// ':0.01616272,"ath_change_percentage":-99.3208,"ath_date":"2018-04-04'
// 'T16:04:48.408Z","atl":4.268e-05,"atl_change_percentage":157.22799,'
// '"atl_date":"2022-05-12T07:28:47.088Z","roi":null,"last_updated":"2'
// '022-08-22T16:38:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"'
// 'Epic Cash","image":"https://assets.coingecko.com/coins/images/9520/'
// 'large/Epic_Coin_NO_drop_shadow.png?1620122642","current_price":2.80'
// '3e-05,"market_cap":415.109,"market_cap_rank":953,"fully_diluted_val'
// 'uation":null,"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h'
// '":2.581e-05,"price_change_24h":1.9e-06,"price_change_percentage_24'
// 'h":7.27524,"market_cap_change_24h":28.26753,"market_cap_change_perc'
// 'entage_24h":7.30726,"circulating_supply":14808052.0,"total_supply":'
// '21000000.0,"max_supply":null,"ath":0.00013848,"ath_change_percentag'
// 'e":-79.75864,"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74028e-0'
// '7,"atl_change_percentage":4783.08078,"atl_date":"2020-03-13T16:55:01'
// '.177Z","roi":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
// 200));
//
// final priceAPI = PriceAPI(client);
// priceAPI.resetLastCalledToForceNextCallToUpdateCache();
//
// // initial fetch to fill cache
// await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
//
// // now this time it should grab from cache instead of http.get
// final cachedPrice =
// await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
//
// expect(
// cachedPrice.toString(),
// '{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0],'
// ' Coin.dogecoin: [0.00000315, -2.68533], Coin.eCash: [0, 0.0], Coin.epicCash: [0.00002803, 7.27524],'
// ' Coin.ethereum: [0, 0.0], Coin.firo: [0.0001096, -0.89304], '
// 'Coin.litecoin: [0, 0.0], Coin.monero: [0.00717236, -0.77656], '
// 'Coin.namecoin: [0, 0.0], Coin.particl: [0, 0.0], Coin.wownero: [0, 0.0], '
// 'Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], '
// 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], '
// 'Coin.firoTestNet: [0, 0.0]}');
//
// // verify only called once during filling of cache
// verify(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
// "=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
// "bitcoin-cash,namecoin,wownero,ethereum,particl"
// "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
// headers: {'Content-Type': 'application/json'})).called(1);
//
// verifyNoMoreInteractions(client);
// });
when(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,bitcoin-cash"
",namecoin,wownero,ethereum,particl,nano,ban&order=market_cap_desc&per_page=50"
"&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
})).thenAnswer((_) async => Response(
'[{"id":"bitcoin","symbol":"btc","name":"Bitcoin","image":"https://asse'
'ts.coingecko.com/coins/images/1/large/bitcoin.png?1547033579","curr'
'ent_price":1.0,"market_cap":19128800,"market_cap_rank":1,"fully_dil'
'uted_valuation":21000000,"total_volume":1272132,"high_24h":1.0,"low'
'_24h":1.0,"price_change_24h":0.0,"price_change_percentage_24h":0.0,'
'"market_cap_change_24h":950.0,"market_cap_change_percentage_24h":0.0'
'0497,"circulating_supply":19128800.0,"total_supply":21000000.0,"max'
'_supply":21000000.0,"ath":1.003301,"ath_change_percentage":-0.32896'
',"ath_date":"2019-10-15T16:00:56.136Z","atl":0.99895134,"atl_change_'
'percentage":0.10498,"atl_date":"2019-10-21T00:00:00.000Z","roi":nul'
'l,"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin","symb'
'ol":"doge","name":"Dogecoin","image":"https://assets.coingecko.com/'
'coins/images/5/large/dogecoin.png?1547792256","current_price":3.15e'
'-06,"market_cap":417916,"market_cap_rank":10,"fully_diluted_valuati'
'on":null,"total_volume":27498,"high_24h":3.26e-06,"low_24h":3.13e-0'
'6,"price_change_24h":-8.6889947714e-08,"price_change_percentage_24h'
'":-2.68533,"market_cap_change_24h":-11370.894861206936,"market_cap_c'
'hange_percentage_24h":-2.64879,"circulating_supply":132670764299.89'
'4,"total_supply":null,"max_supply":null,"ath":1.264e-05,"ath_change'
'_percentage":-75.05046,"ath_date":"2021-05-07T23:04:53.026Z","atl":'
'1.50936e-07,"atl_change_percentage":1989.69346,"atl_date":"2020-12-'
'17T09:18:05.654Z","roi":null,"last_updated":"2022-08-22T16:38:15.11'
'3Z"},{"id":"monero","symbol":"xmr","name":"Monero","image":"https:/'
'/assets.coingecko.com/coins/images/69/large/monero_logo.png?1547033'
'729","current_price":0.00717236,"market_cap":130002,"market_cap_ran'
'k":29,"fully_diluted_valuation":null,"total_volume":4901,"high_24h":'
'0.00731999,"low_24h":0.00707511,"price_change_24h":-5.6133543212467'
'e-05,"price_change_percentage_24h":-0.77656,"market_cap_change_24h"'
':-1007.8447677436197,"market_cap_change_percentage_24h":-0.76929,"c'
'irculating_supply":18147820.3764146,"total_supply":null,"max_supply'
'":null,"ath":0.03475393,"ath_change_percentage":-79.32037,"ath_date'
'":"2018-01-09T00:00:00.000Z","atl":0.00101492,"atl_change_percentag'
'e":608.13327,"atl_date":"2014-12-18T00:00:00.000Z","roi":null,"las'
't_updated":"2022-08-22T16:38:26.347Z"},{"id":"zcoin","symbol":"firo'
'","name":"Firo","image":"https://assets.coingecko.com/coins/images/'
'479/large/firocoingecko.png?1636537544","current_price":0.0001096,"'
'market_cap":1252,"market_cap_rank":604,"fully_diluted_valuation":234'
'9,"total_volume":90.573,"high_24h":0.00011148,"low_24h":0.00010834,'
'"price_change_24h":-9.87561775002e-07,"price_change_percentage_24h'
'":-0.89304,"market_cap_change_24h":-10.046635178462793,"market_cap_'
'change_percentage_24h":-0.79578,"circulating_supply":11411043.83546'
'97,"total_supply":21400000.0,"max_supply":21400000.0,"ath":0.016162'
'72,"ath_change_percentage":-99.3208,"ath_date":"2018-04-04T16:04:48.'
'408Z","atl":4.268e-05,"atl_change_percentage":157.22799,"atl_date":"'
'2022-05-12T07:28:47.088Z","roi":null,"last_updated":"2022-08-22T16'
':38:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"Epic Cash",'
'"image":"https://assets.coingecko.com/coins/images/9520/large/Epic_C'
'oin_NO_drop_shadow.png?1620122642","current_price":2.803e-05,"marke'
't_cap":415.109,"market_cap_rank":953,"fully_diluted_valuation":null'
',"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h":2.581e-05'
',"price_change_24h":1.9e-06,"price_change_percentage_24h":7.27524,"'
'market_cap_change_24h":28.26753,"market_cap_change_percentage_24h":'
'7.30726,"circulating_supply":14808052.0,"total_supply":21000000.0,"'
'max_supply":null,"ath":0.00013848,"ath_change_percentage":-79.75864'
',"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74028e-07,"atl_chang'
'e_percentage":4783.08078,"atl_date":"2020-03-13T16:55:01.177Z","roi'
'":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
200));
// test("response parse failure", () async {
// final client = MockClient();
//
// when(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
// "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
// "bitcoin-cash,namecoin,wownero,ethereum,particl"
// "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
// headers: {
// 'Content-Type': 'application/json'
// })).thenAnswer((_) async => Response(
// '[{"id":"bitcoin","symbol":"btc","name":com/coins/images/1/large/'
// 'bitcoin.png?1547033579","current_price":1.0,"market_cap":19128800'
// ',"market_cap_rank":1,"fully_diluted_valuation":21000000,"total_volum'
// 'e":1272132,"high_24h":1.0,"low_24h":1.0,"price_change_24h":0.0,"pri'
// 'ce_change_percentage_24h":0.0,"market_cap_change_24h":950.0,"market_'
// 'cap_change_percentage_24h":0.00497,"circulating_supply":19128800.0,"t'
// 'otal_supply":21000000.0,"max_supply":21000000.0,"ath":1.003301,"ath'
// '_change_percentage":-0.32896,"ath_date":"2019-10-15T16:00:56.136Z",'
// '"atl":0.99895134,"atl_change_percentage":0.10498,"atl_date":'
// '"2019-10-21T00:00:00.000Z","roi":null,'
// '"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin"'
// ',"symbol":"doge","name":"Dogecoin","image":'
// '"https://assets.coingecko.com/coins/images/5/large/dogecoin.png?1547792256",'
// '"current_price":3.15e-06,"market_cap":417916,"market_cap_rank":10'
// ',"fully_diluted_valuation":null,"total_volume":27498,"high_24h":3'
// '.26e-06,"low_24h":3.13e-06,"price_change_24h":-8.6889947714e-08,"'
// 'price_change_percentage_24h":-2.68533,"market_cap_change_24h":-11'
// '370.894861206936,"market_cap_change_percentage_24h":-2.64879,"cir'
// 'culating_supply":132670764299.894,"total_supply":null,"max_supply'
// '":null,"ath":1.264e-05,"ath_change_percentage":-75.05046,"ath_date'
// '":"2021-05-07T23:04:53.026Z","atl":1.50936e-07,"atl_change_percen'
// 'tage":1989.69346,"atl_date":"2020-12-17T09:18:05.654Z","roi":null,'
// '"last_updated":"2022-08-22T16:38:15.113Z"},{"id":"monero","symbol"'
// ':"xmr","name":"Monero","image":"https://assets.coingecko.com/coins'
// '/images/69/large/monero_logo.png?1547033729","current_price":0.007'
// '17236,"market_cap":130002,"market_cap_rank":29,"fully_diluted_valu'
// 'ation":null,"total_volume":4901,"high_24h":0.00731999,"low_24h":0.'
// '00707511,"price_change_24h":-5.6133543212467e-05,"price_change_per'
// 'centage_24h":-0.77656,"market_cap_change_24h":-1007.8447677436197'
// ',"market_cap_change_percentage_24h":-0.76929,"circulating_supply":'
// '18147820.3764146,"total_supply":null,"max_supply":null,"ath":0.034'
// '75393,"ath_change_percentage":-79.32037,"ath_date":"2018-01-09T00:'
// '00:00.000Z","atl":0.00101492,"atl_change_percentage":608.13327,"at'
// 'l_date":"2014-12-18T00:00:00.000Z","roi":null,"last_updated":"2022'
// '-08-22T16:38:26.347Z"},{"id":"zcoin","symbol":"firo","name":"Firo"'
// ',"image":"https://assets.coingecko.com/coins/images/479/large/firo'
// 'coingecko.png?1636537544","current_price":0.0001096,"market_cap":1'
// '252,"market_cap_rank":604,"fully_diluted_valuation":2349,"total_vo'
// 'lume":90.573,"high_24h":0.00011148,"low_24h":0.00010834,"price_chang'
// 'e_24h":-9.87561775002e-07,"price_change_percentage_24h":-0.89304,'
// '"market_cap_change_24h":-10.046635178462793,"market_cap_change_per'
// 'centage_24h":-0.79578,"circulating_supply":11411043.8354697,"tota'
// 'l_supply":21400000.0,"max_supply":21400000.0,"ath":0.01616272,"ath'
// '_change_percentage":-99.3208,"ath_date":"2018-04-04T16:04:48.408Z"'
// ',"atl":4.268e-05,"atl_change_percentage":157.22799,"atl_date":"202'
// '2-05-12T07:28:47.088Z","roi":null,"last_updated":"2022-08-22T16:3'
// '8:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"Epic Cash",'
// '"image":"https://assets.coingecko.com/coins/images/9520/large/'
// 'Epic_Coin_NO_drop_shadow.png?1620122642","current_price":2.803e-0'
// '5,"market_cap":415.109,"market_cap_rank":953,"fully_diluted_valuat'
// 'ion":null,"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h":'
// '2.581e-05,"price_change_24h":1.9e-06,"price_change_percentage_24h"'
// ':7.27524,"market_cap_change_24h":28.26753,"market_cap_change_per'
// 'centage_24h":7.30726,"circulating_supply":14808052.0,"total_suppl'
// 'y":21000000.0,"max_supply":null,"ath":0.00013848,"ath_change_perce'
// 'ntage":-79.75864,"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74'
// '028e-07,"atl_change_percentage":4783.08078,"atl_date":"2020-03-13T'
// '16:55:01.177Z","roi":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
// 200));
//
// final priceAPI = PriceAPI(client);
// priceAPI.resetLastCalledToForceNextCallToUpdateCache();
//
// final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
//
// expect(
// price.toString(),
// '{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: '
// '[0, 0.0], Coin.eCash: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.ethereum: [0, 0.0],'
// ' Coin.firo: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.monero: [0, 0.0],'
// ' Coin.namecoin: [0, 0.0], Coin.particl: [0, 0.0], Coin.wownero: [0, 0.0],'
// ' Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], '
// 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0],'
// ' Coin.firoTestNet: [0, 0.0]}');
// });
final priceAPI = PriceAPI(client);
priceAPI.resetLastCalledToForceNextCallToUpdateCache();
// test("no internet available", () async {
// final client = MockClient();
//
// when(client.get(
// Uri.parse(
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
// "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
// "bitcoin-cash,namecoin,wownero,ethereum,particl"
// "&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
// headers: {
// 'Content-Type': 'application/json'
// })).thenThrow(const SocketException(
// "Failed host lookup: 'api.com' (OS Error: Temporary failure in name resolution, errno = -3)"));
//
// final priceAPI = PriceAPI(client);
// priceAPI.resetLastCalledToForceNextCallToUpdateCache();
//
// final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
//
// expect(
// price.toString(),
// '{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], '
// 'Coin.dogecoin: [0, 0.0], Coin.eCash: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.ethereum: [0, 0.0],'
// ' Coin.firo: [0, 0.0], Coin.litecoin: [0, 0.0], Coin.monero: [0, 0.0],'
// ' Coin.namecoin: [0, 0.0], Coin.particl: [0, 0.0], Coin.wownero: [0, 0.0],'
// ' Coin.bitcoinTestNet: [0, 0.0], Coin.litecoinTestNet: [0, 0.0], '
// 'Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], '
// 'Coin.firoTestNet: [0, 0.0]}');
// });
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [1, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0.00000315, -2.68533], '
'Coin.eCash: [0, 0.0], '
'Coin.epicCash: [0.00002803, 7.27524], '
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0.0001096, -0.89304], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]'
'}',
);
verify(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false",
),
headers: {'Content-Type': 'application/json'})).called(1);
verifyNoMoreInteractions(client);
});
test("cached price fetch", () async {
final client = MockClient();
when(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&"
"ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
})).thenAnswer((_) async => Response(
'[{"id":"bitcoin","symbol":"btc","name":"Bitcoin","image":"https://a'
'ssets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579","c'
'urrent_price":1.0,"market_cap":19128800,"market_cap_rank":1,"fully_'
'diluted_valuation":21000000,"total_volume":1272132,"high_24h":1.0,"'
'low_24h":1.0,"price_change_24h":0.0,"price_change_percentage_24h":0'
'.0,"market_cap_change_24h":950.0,"market_cap_change_percentage_24h"'
':0.00497,"circulating_supply":19128800.0,"total_supply":21000000.0,"'
'max_supply":21000000.0,"ath":1.003301,"ath_change_percentage":-0.32'
'896,"ath_date":"2019-10-15T16:00:56.136Z","atl":0.99895134,"atl_cha'
'nge_percentage":0.10498,"atl_date":"2019-10-21T00:00:00.000Z","roi"'
':null,"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin","'
'symbol":"doge","name":"Dogecoin","image":"https://assets.coingecko.'
'com/coins/images/5/large/dogecoin.png?1547792256","current_price":3'
'.15e-06,"market_cap":417916,"market_cap_rank":10,"fully_diluted_val'
'uation":null,"total_volume":27498,"high_24h":3.26e-06,"low_24h":3.1'
'3e-06,"price_change_24h":-8.6889947714e-08,"price_change_percentage'
'_24h":-2.68533,"market_cap_change_24h":-11370.894861206936,"market_'
'cap_change_percentage_24h":-2.64879,"circulating_supply":1326707642'
'99.894,"total_supply":null,"max_supply":null,"ath":1.264e-05,"ath_c'
'hange_percentage":-75.05046,"ath_date":"2021-05-07T23:04:53.026Z","'
'atl":1.50936e-07,"atl_change_percentage":1989.69346,"atl_date":"202'
'0-12-17T09:18:05.654Z","roi":null,"last_updated":"2022-08-22T16:38:'
'15.113Z"},{"id":"monero","symbol":"xmr","name":"Monero","image":"ht'
'tps://assets.coingecko.com/coins/images/69/large/monero_logo.png?15'
'47033729","current_price":0.00717236,"market_cap":130002,"market_cap'
'_rank":29,"fully_diluted_valuation":null,"total_volume":4901,"high'
'_24h":0.00731999,"low_24h":0.00707511,"price_change_24h":-5.613354'
'3212467e-05,"price_change_percentage_24h":-0.77656,"market_cap_chan'
'ge_24h":-1007.8447677436197,"market_cap_change_percentage_24h":-0.7'
'6929,"circulating_supply":18147820.3764146,"total_supply":null,"ma'
'x_supply":null,"ath":0.03475393,"ath_change_percentage":-79.32037,"'
'ath_date":"2018-01-09T00:00:00.000Z","atl":0.00101492,"atl_change_'
'percentage":608.13327,"atl_date":"2014-12-18T00:00:00.000Z","roi":n'
'ull,"last_updated":"2022-08-22T16:38:26.347Z"},{"id":"zcoin","symbo'
'l":"firo","name":"Firo","image":"https://assets.coingecko.com/coins'
'/images/479/large/firocoingecko.png?1636537544","current_price":0.0'
'001096,"market_cap":1252,"market_cap_rank":604,"fully_diluted_valu'
'ation":2349,"total_volume":90.573,"high_24h":0.00011148,"low_24h":0'
'.00010834,"price_change_24h":-9.87561775002e-07,"price_change_perce'
'ntage_24h":-0.89304,"market_cap_change_24h":-10.046635178462793,"ma'
'rket_cap_change_percentage_24h":-0.79578,"circulating_supply":11411'
'043.8354697,"total_supply":21400000.0,"max_supply":21400000.0,"ath"'
':0.01616272,"ath_change_percentage":-99.3208,"ath_date":"2018-04-04'
'T16:04:48.408Z","atl":4.268e-05,"atl_change_percentage":157.22799,'
'"atl_date":"2022-05-12T07:28:47.088Z","roi":null,"last_updated":"2'
'022-08-22T16:38:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"'
'Epic Cash","image":"https://assets.coingecko.com/coins/images/9520/'
'large/Epic_Coin_NO_drop_shadow.png?1620122642","current_price":2.80'
'3e-05,"market_cap":415.109,"market_cap_rank":953,"fully_diluted_val'
'uation":null,"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h'
'":2.581e-05,"price_change_24h":1.9e-06,"price_change_percentage_24'
'h":7.27524,"market_cap_change_24h":28.26753,"market_cap_change_perc'
'entage_24h":7.30726,"circulating_supply":14808052.0,"total_supply":'
'21000000.0,"max_supply":null,"ath":0.00013848,"ath_change_percentag'
'e":-79.75864,"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74028e-0'
'7,"atl_change_percentage":4783.08078,"atl_date":"2020-03-13T16:55:01'
'.177Z","roi":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
200));
final priceAPI = PriceAPI(client);
priceAPI.resetLastCalledToForceNextCallToUpdateCache();
// initial fetch to fill cache
await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
// now this time it should grab from cache instead of http.get
final cachedPrice =
await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(
cachedPrice.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [1, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0.00000315, -2.68533], '
'Coin.eCash: [0, 0.0], '
'Coin.epicCash: [0.00002803, 7.27524], '
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0.0001096, -0.89304], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0.00717236, -0.77656], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]'
'}',
);
// verify only called once during filling of cache
verify(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids"
"=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {'Content-Type': 'application/json'})).called(1);
verifyNoMoreInteractions(client);
});
test("response parse failure", () async {
final client = MockClient();
when(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
})).thenAnswer((_) async => Response(
'[{"id":"bitcoin","symbol":"btc","name":com/coins/images/1/large/'
'bitcoin.png?1547033579","current_price":1.0,"market_cap":19128800'
',"market_cap_rank":1,"fully_diluted_valuation":21000000,"total_volum'
'e":1272132,"high_24h":1.0,"low_24h":1.0,"price_change_24h":0.0,"pri'
'ce_change_percentage_24h":0.0,"market_cap_change_24h":950.0,"market_'
'cap_change_percentage_24h":0.00497,"circulating_supply":19128800.0,"t'
'otal_supply":21000000.0,"max_supply":21000000.0,"ath":1.003301,"ath'
'_change_percentage":-0.32896,"ath_date":"2019-10-15T16:00:56.136Z",'
'"atl":0.99895134,"atl_change_percentage":0.10498,"atl_date":'
'"2019-10-21T00:00:00.000Z","roi":null,'
'"last_updated":"2022-08-22T16:37:59.237Z"},{"id":"dogecoin"'
',"symbol":"doge","name":"Dogecoin","image":'
'"https://assets.coingecko.com/coins/images/5/large/dogecoin.png?1547792256",'
'"current_price":3.15e-06,"market_cap":417916,"market_cap_rank":10'
',"fully_diluted_valuation":null,"total_volume":27498,"high_24h":3'
'.26e-06,"low_24h":3.13e-06,"price_change_24h":-8.6889947714e-08,"'
'price_change_percentage_24h":-2.68533,"market_cap_change_24h":-11'
'370.894861206936,"market_cap_change_percentage_24h":-2.64879,"cir'
'culating_supply":132670764299.894,"total_supply":null,"max_supply'
'":null,"ath":1.264e-05,"ath_change_percentage":-75.05046,"ath_date'
'":"2021-05-07T23:04:53.026Z","atl":1.50936e-07,"atl_change_percen'
'tage":1989.69346,"atl_date":"2020-12-17T09:18:05.654Z","roi":null,'
'"last_updated":"2022-08-22T16:38:15.113Z"},{"id":"monero","symbol"'
':"xmr","name":"Monero","image":"https://assets.coingecko.com/coins'
'/images/69/large/monero_logo.png?1547033729","current_price":0.007'
'17236,"market_cap":130002,"market_cap_rank":29,"fully_diluted_valu'
'ation":null,"total_volume":4901,"high_24h":0.00731999,"low_24h":0.'
'00707511,"price_change_24h":-5.6133543212467e-05,"price_change_per'
'centage_24h":-0.77656,"market_cap_change_24h":-1007.8447677436197'
',"market_cap_change_percentage_24h":-0.76929,"circulating_supply":'
'18147820.3764146,"total_supply":null,"max_supply":null,"ath":0.034'
'75393,"ath_change_percentage":-79.32037,"ath_date":"2018-01-09T00:'
'00:00.000Z","atl":0.00101492,"atl_change_percentage":608.13327,"at'
'l_date":"2014-12-18T00:00:00.000Z","roi":null,"last_updated":"2022'
'-08-22T16:38:26.347Z"},{"id":"zcoin","symbol":"firo","name":"Firo"'
',"image":"https://assets.coingecko.com/coins/images/479/large/firo'
'coingecko.png?1636537544","current_price":0.0001096,"market_cap":1'
'252,"market_cap_rank":604,"fully_diluted_valuation":2349,"total_vo'
'lume":90.573,"high_24h":0.00011148,"low_24h":0.00010834,"price_chang'
'e_24h":-9.87561775002e-07,"price_change_percentage_24h":-0.89304,'
'"market_cap_change_24h":-10.046635178462793,"market_cap_change_per'
'centage_24h":-0.79578,"circulating_supply":11411043.8354697,"tota'
'l_supply":21400000.0,"max_supply":21400000.0,"ath":0.01616272,"ath'
'_change_percentage":-99.3208,"ath_date":"2018-04-04T16:04:48.408Z"'
',"atl":4.268e-05,"atl_change_percentage":157.22799,"atl_date":"202'
'2-05-12T07:28:47.088Z","roi":null,"last_updated":"2022-08-22T16:3'
'8:47.229Z"},{"id":"epic-cash","symbol":"epic","name":"Epic Cash",'
'"image":"https://assets.coingecko.com/coins/images/9520/large/'
'Epic_Coin_NO_drop_shadow.png?1620122642","current_price":2.803e-0'
'5,"market_cap":415.109,"market_cap_rank":953,"fully_diluted_valuat'
'ion":null,"total_volume":0.2371557,"high_24h":3.053e-05,"low_24h":'
'2.581e-05,"price_change_24h":1.9e-06,"price_change_percentage_24h"'
':7.27524,"market_cap_change_24h":28.26753,"market_cap_change_per'
'centage_24h":7.30726,"circulating_supply":14808052.0,"total_suppl'
'y":21000000.0,"max_supply":null,"ath":0.00013848,"ath_change_perce'
'ntage":-79.75864,"ath_date":"2021-12-11T08:39:41.129Z","atl":5.74'
'028e-07,"atl_change_percentage":4783.08078,"atl_date":"2020-03-13T'
'16:55:01.177Z","roi":null,"last_updated":"2022-08-22T16:38:32.826Z"}]',
200));
final priceAPI = PriceAPI(client);
priceAPI.resetLastCalledToForceNextCallToUpdateCache();
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0, 0.0], '
'Coin.eCash: [0, 0.0], '
'Coin.epicCash: [0, 0.0], '
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0, 0.0], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]'
'}',
);
});
test("no internet available", () async {
final client = MockClient();
when(client.get(
Uri.parse(
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc"
"&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin,"
"bitcoin-cash,namecoin,wownero,ethereum,particl,nano,ban"
"&order=market_cap_desc&per_page=50&page=1&sparkline=false"),
headers: {
'Content-Type': 'application/json'
})).thenThrow(const SocketException(
"Failed host lookup: 'api.com' (OS Error: Temporary failure in name resolution, errno = -3)"));
final priceAPI = PriceAPI(client);
priceAPI.resetLastCalledToForceNextCallToUpdateCache();
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
expect(
price.toString(),
'{'
'Coin.banano: [0, 0.0], '
'Coin.bitcoin: [0, 0.0], '
'Coin.bitcoincash: [0, 0.0], '
'Coin.dogecoin: [0, 0.0], '
'Coin.eCash: [0, 0.0], '
'Coin.epicCash: [0, 0.0], '
'Coin.ethereum: [0, 0.0], '
'Coin.firo: [0, 0.0], '
'Coin.litecoin: [0, 0.0], '
'Coin.monero: [0, 0.0], '
'Coin.namecoin: [0, 0.0], '
'Coin.nano: [0, 0.0], '
'Coin.particl: [0, 0.0], '
'Coin.wownero: [0, 0.0], '
'Coin.bitcoinTestNet: [0, 0.0], '
'Coin.bitcoincashTestnet: [0, 0.0], '
'Coin.dogecoinTestNet: [0, 0.0], '
'Coin.firoTestNet: [0, 0.0], '
'Coin.litecoinTestNet: [0, 0.0]'
'}',
);
});
tearDown(() async {
await tearDownTestHive();

View file

@ -269,7 +269,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i13.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i13.Coin.bitcoin,
returnValue: _i13.Coin.banano,
) as _i13.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -230,7 +230,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i11.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i11.Coin.bitcoin,
returnValue: _i11.Coin.banano,
) as _i11.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -228,7 +228,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i11.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i11.Coin.bitcoin,
returnValue: _i11.Coin.banano,
) as _i11.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -548,7 +548,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
@override
_i9.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i9.Coin.bitcoin,
returnValue: _i9.Coin.banano,
) as _i9.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -548,7 +548,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
@override
_i9.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i9.Coin.bitcoin,
returnValue: _i9.Coin.banano,
) as _i9.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -389,7 +389,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
@override
_i11.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i11.Coin.bitcoin,
returnValue: _i11.Coin.banano,
) as _i11.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -317,7 +317,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i10.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i10.Coin.bitcoin,
returnValue: _i10.Coin.banano,
) as _i10.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -317,7 +317,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i10.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i10.Coin.bitcoin,
returnValue: _i10.Coin.banano,
) as _i10.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -569,7 +569,7 @@ class MockManager extends _i1.Mock implements _i15.Manager {
@override
_i9.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i9.Coin.bitcoin,
returnValue: _i9.Coin.banano,
) as _i9.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
@override
_i8.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i8.Coin.bitcoin,
returnValue: _i8.Coin.banano,
) as _i8.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -104,7 +104,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -103,7 +103,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -144,7 +144,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i10.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i10.Coin.bitcoin,
returnValue: _i10.Coin.banano,
) as _i10.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -104,7 +104,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i7.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i7.Coin.bitcoin,
returnValue: _i7.Coin.banano,
) as _i7.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -217,7 +217,7 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet {
@override
_i12.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i12.Coin.bitcoin,
returnValue: _i12.Coin.banano,
) as _i12.Coin);
@override
_i11.Future<List<String>> get mnemonic => (super.noSuchMethod(

View file

@ -17,8 +17,10 @@ import 'package:stackwallet/services/wallets_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_service.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_unit.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/listenable_list.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/widgets/managed_favorite.dart';
import '../sample_data/theme_json.dart';
@ -36,6 +38,7 @@ Amount _a(int i) => Amount.fromDecimal(
WalletsService,
BitcoinWallet,
ThemeService,
Prefs,
LocaleService
], customMocks: [
MockSpec<NodeService>(returnNullOnMissingStub: true),
@ -47,6 +50,7 @@ void main() {
final wallets = MockWallets();
final CoinServiceAPI wallet = MockBitcoinWallet();
final mockThemeService = MockThemeService();
final mockPrefs = MockPrefs();
when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
(_) => StackTheme.fromJson(
@ -58,6 +62,13 @@ void main() {
when(wallet.walletName).thenAnswer((_) => "some wallet");
when(wallet.walletId).thenAnswer((_) => "some wallet id");
when(mockPrefs.amountUnit(Coin.bitcoin)).thenAnswer(
(_) => AmountUnit.normal,
);
when(mockPrefs.maxDecimals(Coin.bitcoin)).thenAnswer(
(_) => 8,
);
final manager = Manager(wallet);
when(wallets.getManager("some wallet id"))
.thenAnswer((realInvocation) => manager);
@ -78,6 +89,7 @@ void main() {
overrides: [
walletsChangeNotifierProvider.overrideWithValue(wallets),
pThemeService.overrideWithValue(mockThemeService),
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
],
child: MaterialApp(
theme: ThemeData(
@ -103,161 +115,179 @@ void main() {
expect(find.byType(ManagedFavorite), findsOneWidget);
});
// testWidgets("Button Pressed - wallet unfavorite", (widgetTester) async {
// final wallets = MockWallets();
// final CoinServiceAPI wallet = MockBitcoinWallet();
// final mockLocaleService = MockLocaleService();
// final mockWalletsService = MockWalletsService();
// final mockThemeService = MockThemeService();
//
// when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
// (_) => StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// );
// when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
// when(wallet.walletName).thenAnswer((_) => "some wallet");
// when(wallet.walletId).thenAnswer((_) => "some wallet id");
//
// final manager = Manager(wallet);
//
// when(wallets.getManager("some wallet id"))
// .thenAnswer((realInvocation) => manager);
// when(manager.balance).thenAnswer(
// (realInvocation) => Balance(
// total: _a(10),
// spendable: _a(10),
// blockedTotal: _a(0),
// pendingSpendable: _a(0),
// ),
// );
//
// when(manager.isFavorite).thenAnswer((realInvocation) => false);
//
// when(mockLocaleService.locale).thenAnswer((_) => "en_US");
//
// when(wallets.getManagerProvider("some wallet id")).thenAnswer(
// (realInvocation) => ChangeNotifierProvider((ref) => manager));
//
// const managedFavorite = ManagedFavorite(walletId: "some wallet id");
//
// final ListenableList<ChangeNotifierProvider<Manager>> favorites =
// ListenableList();
//
// final ListenableList<ChangeNotifierProvider<Manager>> nonfavorites =
// ListenableList();
// await widgetTester.pumpWidget(
// ProviderScope(
// overrides: [
// walletsChangeNotifierProvider.overrideWithValue(wallets),
// localeServiceChangeNotifierProvider
// .overrideWithValue(mockLocaleService),
// favoritesProvider.overrideWithValue(favorites),
// nonFavoritesProvider.overrideWithValue(nonfavorites),
// pThemeService.overrideWithValue(mockThemeService),
// walletsServiceChangeNotifierProvider
// .overrideWithValue(mockWalletsService)
// ],
// child: MaterialApp(
// theme: ThemeData(
// extensions: [
// StackColors.fromStackColorTheme(
// StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// ),
// ],
// ),
// home: const Material(
// child: managedFavorite,
// ),
// ),
// ),
// );
//
// expect(find.byType(RawMaterialButton), findsOneWidget);
// await widgetTester.tap(find.byType(RawMaterialButton));
// await widgetTester.pump();
// });
testWidgets("Button Pressed - wallet unfavorite", (widgetTester) async {
final wallets = MockWallets();
final CoinServiceAPI wallet = MockBitcoinWallet();
final mockLocaleService = MockLocaleService();
final mockWalletsService = MockWalletsService();
final mockThemeService = MockThemeService();
final mockPrefs = MockPrefs();
// testWidgets("Button Pressed - wallet is favorite", (widgetTester) async {
// final wallets = MockWallets();
// final CoinServiceAPI wallet = MockBitcoinWallet();
// final mockLocaleService = MockLocaleService();
// final mockWalletsService = MockWalletsService();
// final mockThemeService = MockThemeService();
//
// when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
// (_) => StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// );
// when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
// when(wallet.walletName).thenAnswer((_) => "some wallet");
// when(wallet.walletId).thenAnswer((_) => "some wallet id");
//
// final manager = Manager(wallet);
//
// when(wallets.getManager("some wallet id"))
// .thenAnswer((realInvocation) => manager);
//
// when(manager.isFavorite).thenAnswer((realInvocation) => true);
// when(manager.balance).thenAnswer(
// (realInvocation) => Balance(
// total: _a(10),
// spendable: _a(10),
// blockedTotal: _a(0),
// pendingSpendable: _a(0),
// ),
// );
//
// when(mockLocaleService.locale).thenAnswer((_) => "en_US");
//
// when(wallets.getManagerProvider("some wallet id")).thenAnswer(
// (realInvocation) => ChangeNotifierProvider((ref) => manager));
//
// const managedFavorite = ManagedFavorite(walletId: "some wallet id");
//
// final ListenableList<ChangeNotifierProvider<Manager>> favorites =
// ListenableList();
//
// final ListenableList<ChangeNotifierProvider<Manager>> nonfavorites =
// ListenableList();
// await widgetTester.pumpWidget(
// ProviderScope(
// overrides: [
// walletsChangeNotifierProvider.overrideWithValue(wallets),
// localeServiceChangeNotifierProvider
// .overrideWithValue(mockLocaleService),
// favoritesProvider.overrideWithValue(favorites),
// nonFavoritesProvider.overrideWithValue(nonfavorites),
// pThemeService.overrideWithValue(mockThemeService),
// walletsServiceChangeNotifierProvider
// .overrideWithValue(mockWalletsService)
// ],
// child: MaterialApp(
// theme: ThemeData(
// extensions: [
// StackColors.fromStackColorTheme(
// StackTheme.fromJson(
// json: lightThemeJsonMap,
// applicationThemesDirectoryPath: "test",
// ),
// ),
// ],
// ),
// home: const Material(
// child: managedFavorite,
// ),
// ),
// ),
// );
//
// expect(find.byType(RawMaterialButton), findsOneWidget);
// await widgetTester.tap(find.byType(RawMaterialButton));
// await widgetTester.pump();
// });
when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
(_) => StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
);
when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
when(wallet.walletName).thenAnswer((_) => "some wallet");
when(wallet.walletId).thenAnswer((_) => "some wallet id");
when(mockPrefs.amountUnit(Coin.bitcoin)).thenAnswer(
(_) => AmountUnit.normal,
);
final manager = Manager(wallet);
when(wallets.getManager("some wallet id"))
.thenAnswer((realInvocation) => manager);
when(manager.balance).thenAnswer(
(realInvocation) => Balance(
total: _a(10),
spendable: _a(10),
blockedTotal: _a(0),
pendingSpendable: _a(0),
),
);
when(manager.isFavorite).thenAnswer((realInvocation) => false);
when(mockPrefs.maxDecimals(Coin.bitcoin)).thenAnswer(
(_) => 8,
);
when(mockLocaleService.locale).thenAnswer((_) => "en_US");
when(wallets.getManagerProvider("some wallet id")).thenAnswer(
(realInvocation) => ChangeNotifierProvider((ref) => manager));
const managedFavorite = ManagedFavorite(walletId: "some wallet id");
final ListenableList<ChangeNotifierProvider<Manager>> favorites =
ListenableList();
final ListenableList<ChangeNotifierProvider<Manager>> nonfavorites =
ListenableList();
await widgetTester.pumpWidget(
ProviderScope(
overrides: [
walletsChangeNotifierProvider.overrideWithValue(wallets),
localeServiceChangeNotifierProvider
.overrideWithValue(mockLocaleService),
favoritesProvider.overrideWithValue(favorites),
nonFavoritesProvider.overrideWithValue(nonfavorites),
pThemeService.overrideWithValue(mockThemeService),
walletsServiceChangeNotifierProvider
.overrideWithValue(mockWalletsService),
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
],
child: MaterialApp(
theme: ThemeData(
extensions: [
StackColors.fromStackColorTheme(
StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
),
],
),
home: const Material(
child: managedFavorite,
),
),
),
);
expect(find.byType(RawMaterialButton), findsOneWidget);
await widgetTester.tap(find.byType(RawMaterialButton));
await widgetTester.pump();
});
testWidgets("Button Pressed - wallet is favorite", (widgetTester) async {
final wallets = MockWallets();
final CoinServiceAPI wallet = MockBitcoinWallet();
final mockLocaleService = MockLocaleService();
final mockWalletsService = MockWalletsService();
final mockThemeService = MockThemeService();
final mockPrefs = MockPrefs();
when(mockThemeService.getTheme(themeId: "light")).thenAnswer(
(_) => StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
);
when(wallet.coin).thenAnswer((_) => Coin.bitcoin);
when(wallet.walletName).thenAnswer((_) => "some wallet");
when(wallet.walletId).thenAnswer((_) => "some wallet id");
when(mockPrefs.maxDecimals(Coin.bitcoin)).thenAnswer(
(_) => 8,
);
final manager = Manager(wallet);
when(wallets.getManager("some wallet id"))
.thenAnswer((realInvocation) => manager);
when(manager.isFavorite).thenAnswer((realInvocation) => true);
when(manager.balance).thenAnswer(
(realInvocation) => Balance(
total: _a(10),
spendable: _a(10),
blockedTotal: _a(0),
pendingSpendable: _a(0),
),
);
when(mockPrefs.amountUnit(Coin.bitcoin)).thenAnswer(
(_) => AmountUnit.normal,
);
when(mockLocaleService.locale).thenAnswer((_) => "en_US");
when(wallets.getManagerProvider("some wallet id")).thenAnswer(
(realInvocation) => ChangeNotifierProvider((ref) => manager));
const managedFavorite = ManagedFavorite(walletId: "some wallet id");
final ListenableList<ChangeNotifierProvider<Manager>> favorites =
ListenableList();
final ListenableList<ChangeNotifierProvider<Manager>> nonfavorites =
ListenableList();
await widgetTester.pumpWidget(
ProviderScope(
overrides: [
walletsChangeNotifierProvider.overrideWithValue(wallets),
localeServiceChangeNotifierProvider
.overrideWithValue(mockLocaleService),
favoritesProvider.overrideWithValue(favorites),
nonFavoritesProvider.overrideWithValue(nonfavorites),
pThemeService.overrideWithValue(mockThemeService),
prefsChangeNotifierProvider.overrideWithValue(mockPrefs),
walletsServiceChangeNotifierProvider
.overrideWithValue(mockWalletsService)
],
child: MaterialApp(
theme: ThemeData(
extensions: [
StackColors.fromStackColorTheme(
StackTheme.fromJson(
json: lightThemeJsonMap,
applicationThemesDirectoryPath: "test",
),
),
],
),
home: const Material(
child: managedFavorite,
),
),
),
);
expect(find.byType(RawMaterialButton), findsOneWidget);
await widgetTester.tap(find.byType(RawMaterialButton));
await widgetTester.pump();
});
}

View file

@ -21,13 +21,13 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i9;
import 'package:stackwallet/models/balance.dart' as _i11;
import 'package:stackwallet/models/isar/models/isar_models.dart' as _i17;
import 'package:stackwallet/models/isar/stack_theme.dart' as _i31;
import 'package:stackwallet/models/node_model.dart' as _i33;
import 'package:stackwallet/models/node_model.dart' as _i36;
import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i8;
import 'package:stackwallet/models/signing_data.dart' as _i28;
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart' as _i26;
import 'package:stackwallet/services/coins/coin_service.dart' as _i20;
import 'package:stackwallet/services/coins/manager.dart' as _i6;
import 'package:stackwallet/services/locale_service.dart' as _i32;
import 'package:stackwallet/services/locale_service.dart' as _i35;
import 'package:stackwallet/services/node_service.dart' as _i3;
import 'package:stackwallet/services/transaction_notification_tracker.dart'
as _i7;
@ -35,8 +35,11 @@ import 'package:stackwallet/services/wallets.dart' as _i21;
import 'package:stackwallet/services/wallets_service.dart' as _i2;
import 'package:stackwallet/themes/theme_service.dart' as _i30;
import 'package:stackwallet/utilities/amount/amount.dart' as _i14;
import 'package:stackwallet/utilities/amount/amount_unit.dart' as _i34;
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart' as _i33;
import 'package:stackwallet/utilities/enums/coin_enum.dart' as _i22;
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart' as _i27;
import 'package:stackwallet/utilities/enums/sync_type_enum.dart' as _i32;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i19;
import 'package:stackwallet/utilities/prefs.dart' as _i24;
@ -779,7 +782,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
@ -2055,10 +2058,488 @@ class MockThemeService extends _i1.Mock implements _i30.ThemeService {
)) as _i31.StackTheme?);
}
/// A class which mocks [Prefs].
///
/// See the documentation for Mockito's code generation for more information.
class MockPrefs extends _i1.Mock implements _i24.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<String> get walletIdsSyncOnStartup => (super.noSuchMethod(
Invocation.getter(#walletIdsSyncOnStartup),
returnValue: <String>[],
) as List<String>);
@override
set walletIdsSyncOnStartup(List<String>? walletIdsSyncOnStartup) =>
super.noSuchMethod(
Invocation.setter(
#walletIdsSyncOnStartup,
walletIdsSyncOnStartup,
),
returnValueForMissingStub: null,
);
@override
_i32.SyncingType get syncType => (super.noSuchMethod(
Invocation.getter(#syncType),
returnValue: _i32.SyncingType.currentWalletOnly,
) as _i32.SyncingType);
@override
set syncType(_i32.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
bool get randomizePIN => (super.noSuchMethod(
Invocation.getter(#randomizePIN),
returnValue: false,
) as bool);
@override
set randomizePIN(bool? randomizePIN) => super.noSuchMethod(
Invocation.setter(
#randomizePIN,
randomizePIN,
),
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
int get familiarity => (super.noSuchMethod(
Invocation.getter(#familiarity),
returnValue: 0,
) as int);
@override
set familiarity(int? familiarity) => super.noSuchMethod(
Invocation.setter(
#familiarity,
familiarity,
),
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
_i33.BackupFrequencyType get backupFrequencyType => (super.noSuchMethod(
Invocation.getter(#backupFrequencyType),
returnValue: _i33.BackupFrequencyType.everyTenMinutes,
) as _i33.BackupFrequencyType);
@override
set backupFrequencyType(_i33.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 enableCoinControl => (super.noSuchMethod(
Invocation.getter(#enableCoinControl),
returnValue: false,
) as bool);
@override
set enableCoinControl(bool? enableCoinControl) => super.noSuchMethod(
Invocation.setter(
#enableCoinControl,
enableCoinControl,
),
returnValueForMissingStub: null,
);
@override
bool get enableSystemBrightness => (super.noSuchMethod(
Invocation.getter(#enableSystemBrightness),
returnValue: false,
) as bool);
@override
set enableSystemBrightness(bool? enableSystemBrightness) =>
super.noSuchMethod(
Invocation.setter(
#enableSystemBrightness,
enableSystemBrightness,
),
returnValueForMissingStub: null,
);
@override
String get themeId => (super.noSuchMethod(
Invocation.getter(#themeId),
returnValue: '',
) as String);
@override
set themeId(String? themeId) => super.noSuchMethod(
Invocation.setter(
#themeId,
themeId,
),
returnValueForMissingStub: null,
);
@override
String get systemBrightnessLightThemeId => (super.noSuchMethod(
Invocation.getter(#systemBrightnessLightThemeId),
returnValue: '',
) as String);
@override
set systemBrightnessLightThemeId(String? systemBrightnessLightThemeId) =>
super.noSuchMethod(
Invocation.setter(
#systemBrightnessLightThemeId,
systemBrightnessLightThemeId,
),
returnValueForMissingStub: null,
);
@override
String get systemBrightnessDarkThemeId => (super.noSuchMethod(
Invocation.getter(#systemBrightnessDarkThemeId),
returnValue: '',
) as String);
@override
set systemBrightnessDarkThemeId(String? systemBrightnessDarkThemeId) =>
super.noSuchMethod(
Invocation.setter(
#systemBrightnessDarkThemeId,
systemBrightnessDarkThemeId,
),
returnValueForMissingStub: null,
);
@override
bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners),
returnValue: false,
) as bool);
@override
_i23.Future<void> init() => (super.noSuchMethod(
Invocation.method(
#init,
[],
),
returnValue: _i23.Future<void>.value(),
returnValueForMissingStub: _i23.Future<void>.value(),
) as _i23.Future<void>);
@override
_i23.Future<void> incrementCurrentNotificationIndex() => (super.noSuchMethod(
Invocation.method(
#incrementCurrentNotificationIndex,
[],
),
returnValue: _i23.Future<void>.value(),
returnValueForMissingStub: _i23.Future<void>.value(),
) as _i23.Future<void>);
@override
_i23.Future<bool> isExternalCallsSet() => (super.noSuchMethod(
Invocation.method(
#isExternalCallsSet,
[],
),
returnValue: _i23.Future<bool>.value(false),
) as _i23.Future<bool>);
@override
_i23.Future<void> saveUserID(String? userId) => (super.noSuchMethod(
Invocation.method(
#saveUserID,
[userId],
),
returnValue: _i23.Future<void>.value(),
returnValueForMissingStub: _i23.Future<void>.value(),
) as _i23.Future<void>);
@override
_i23.Future<void> saveSignupEpoch(int? signupEpoch) => (super.noSuchMethod(
Invocation.method(
#saveSignupEpoch,
[signupEpoch],
),
returnValue: _i23.Future<void>.value(),
returnValueForMissingStub: _i23.Future<void>.value(),
) as _i23.Future<void>);
@override
_i34.AmountUnit amountUnit(_i22.Coin? coin) => (super.noSuchMethod(
Invocation.method(
#amountUnit,
[coin],
),
returnValue: _i34.AmountUnit.normal,
) as _i34.AmountUnit);
@override
void updateAmountUnit({
required _i22.Coin? coin,
required _i34.AmountUnit? amountUnit,
}) =>
super.noSuchMethod(
Invocation.method(
#updateAmountUnit,
[],
{
#coin: coin,
#amountUnit: amountUnit,
},
),
returnValueForMissingStub: null,
);
@override
int maxDecimals(_i22.Coin? coin) => (super.noSuchMethod(
Invocation.method(
#maxDecimals,
[coin],
),
returnValue: 0,
) as int);
@override
void updateMaxDecimals({
required _i22.Coin? coin,
required int? maxDecimals,
}) =>
super.noSuchMethod(
Invocation.method(
#updateMaxDecimals,
[],
{
#coin: coin,
#maxDecimals: maxDecimals,
},
),
returnValueForMissingStub: null,
);
@override
void addListener(_i25.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
[listener],
),
returnValueForMissingStub: null,
);
@override
void removeListener(_i25.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 [LocaleService].
///
/// See the documentation for Mockito's code generation for more information.
class MockLocaleService extends _i1.Mock implements _i32.LocaleService {
class MockLocaleService extends _i1.Mock implements _i35.LocaleService {
MockLocaleService() {
_i1.throwOnMissingStub(this);
}
@ -2130,15 +2611,15 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
),
) as _i19.SecureStorageInterface);
@override
List<_i33.NodeModel> get primaryNodes => (super.noSuchMethod(
List<_i36.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),
returnValue: <_i33.NodeModel>[],
) as List<_i33.NodeModel>);
returnValue: <_i36.NodeModel>[],
) as List<_i36.NodeModel>);
@override
List<_i33.NodeModel> get nodes => (super.noSuchMethod(
List<_i36.NodeModel> get nodes => (super.noSuchMethod(
Invocation.getter(#nodes),
returnValue: <_i33.NodeModel>[],
) as List<_i33.NodeModel>);
returnValue: <_i36.NodeModel>[],
) as List<_i36.NodeModel>);
@override
bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners),
@ -2156,7 +2637,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override
_i23.Future<void> setPrimaryNodeFor({
required _i22.Coin? coin,
required _i33.NodeModel? node,
required _i36.NodeModel? node,
bool? shouldNotifyListeners = false,
}) =>
(super.noSuchMethod(
@ -2173,40 +2654,40 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
returnValueForMissingStub: _i23.Future<void>.value(),
) as _i23.Future<void>);
@override
_i33.NodeModel? getPrimaryNodeFor({required _i22.Coin? coin}) =>
_i36.NodeModel? getPrimaryNodeFor({required _i22.Coin? coin}) =>
(super.noSuchMethod(Invocation.method(
#getPrimaryNodeFor,
[],
{#coin: coin},
)) as _i33.NodeModel?);
)) as _i36.NodeModel?);
@override
List<_i33.NodeModel> getNodesFor(_i22.Coin? coin) => (super.noSuchMethod(
List<_i36.NodeModel> getNodesFor(_i22.Coin? coin) => (super.noSuchMethod(
Invocation.method(
#getNodesFor,
[coin],
),
returnValue: <_i33.NodeModel>[],
) as List<_i33.NodeModel>);
returnValue: <_i36.NodeModel>[],
) as List<_i36.NodeModel>);
@override
_i33.NodeModel? getNodeById({required String? id}) =>
_i36.NodeModel? getNodeById({required String? id}) =>
(super.noSuchMethod(Invocation.method(
#getNodeById,
[],
{#id: id},
)) as _i33.NodeModel?);
)) as _i36.NodeModel?);
@override
List<_i33.NodeModel> failoverNodesFor({required _i22.Coin? coin}) =>
List<_i36.NodeModel> failoverNodesFor({required _i22.Coin? coin}) =>
(super.noSuchMethod(
Invocation.method(
#failoverNodesFor,
[],
{#coin: coin},
),
returnValue: <_i33.NodeModel>[],
) as List<_i33.NodeModel>);
returnValue: <_i36.NodeModel>[],
) as List<_i36.NodeModel>);
@override
_i23.Future<void> add(
_i33.NodeModel? node,
_i36.NodeModel? node,
String? password,
bool? shouldNotifyListeners,
) =>
@ -2258,7 +2739,7 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
) as _i23.Future<void>);
@override
_i23.Future<void> edit(
_i33.NodeModel? editedNode,
_i36.NodeModel? editedNode,
String? password,
bool? shouldNotifyListeners,
) =>
@ -2350,7 +2831,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(
@ -2718,7 +3199,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -866,7 +866,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i28.BitcoinWallet {
@override
_i21.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i21.Coin.bitcoin,
returnValue: _i21.Coin.banano,
) as _i21.Coin);
@override
_i22.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
@ -2077,7 +2077,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i21.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i21.Coin.bitcoin,
returnValue: _i21.Coin.banano,
) as _i21.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(
@ -2445,7 +2445,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
@override
_i21.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i21.Coin.bitcoin,
returnValue: _i21.Coin.banano,
) as _i21.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -521,7 +521,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
@override
_i20.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i20.Coin.bitcoin,
returnValue: _i20.Coin.banano,
) as _i20.Coin);
@override
_i21.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(

View file

@ -776,7 +776,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
@ -2186,7 +2186,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(
@ -2554,7 +2554,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(

View file

@ -878,7 +878,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i29.BitcoinWallet {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
@ -2289,7 +2289,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(
@ -2657,7 +2657,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
@override
_i22.Coin get coin => (super.noSuchMethod(
Invocation.getter(#coin),
returnValue: _i22.Coin.bitcoin,
returnValue: _i22.Coin.banano,
) as _i22.Coin);
@override
bool get isRefreshing => (super.noSuchMethod(