Merge branch 'master' into wexp

This commit is contained in:
Marcos Rodriguez Vélez 2024-07-25 21:51:33 -04:00 committed by GitHub
commit 72619e4c86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 216 additions and 157 deletions

View file

@ -3,7 +3,7 @@ jobs:
lint:
docker:
- image: cimg/node:16.20.2
- image: cimg/node:20.16.0
working_directory: ~/repo
@ -26,7 +26,7 @@ jobs:
unit:
docker:
- image: cimg/node:16.20.2
- image: cimg/node:20.16.0
working_directory: ~/repo
@ -50,7 +50,10 @@ jobs:
integration:
docker:
- image: cimg/node:16.20.2
- image: cimg/node:20.16.0
environment:
RETRY: "1"
working_directory: ~/repo
@ -71,7 +74,7 @@ jobs:
# run tests!
- run:
command: npm run jest || npm run jest || npm run jest
command: npm run jest || npm run jest || npm run jest || npm run jest
# Orchestrate our job run sequence
workflows:

View file

@ -42,7 +42,7 @@ jobs:
run: npm install
- name: Run tests
run: npm test || npm test || npm test
run: npm test || npm test || npm test || npm test
env:
BIP47_HD_MNEMONIC: ${{ secrets.BIP47_HD_MNEMONIC}}
HD_MNEMONIC: ${{ secrets.HD_MNEMONIC }}
@ -53,6 +53,7 @@ jobs:
FAULTY_ZPUB: ${{ secrets.FAULTY_ZPUB }}
MNEMONICS_COBO: ${{ secrets.MNEMONICS_COBO }}
MNEMONICS_COLDCARD: ${{ secrets.MNEMONICS_COLDCARD }}
RETRY: 1
e2e:
runs-on: ubuntu-latest
@ -65,9 +66,7 @@ jobs:
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
tool-cache: true
android: false
dotnet: true
haskell: true
@ -146,4 +145,4 @@ jobs:
if: failure()
with:
name: e2e-test-videos
path: /mnt/artifacts/
path: /mnt/artifacts/

View file

@ -10,7 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit

View file

@ -288,7 +288,6 @@ interface WalletsCarouselProps extends Partial<FlatListProps<any>> {
handleLongPress?: () => void;
data: TWallet[];
scrollEnabled?: boolean;
showNewWalletPanel?: boolean; // New prop
}
type FlatListRefType = FlatList<any> & {
@ -317,7 +316,7 @@ const cStyles = StyleSheet.create({
const ListHeaderComponent: React.FC = () => <View style={cStyles.separatorStyle} />;
const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props, ref) => {
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled, showNewWalletPanel, onNewWalletPress } = props;
const { horizontal, data, handleLongPress, onPress, selectedWallet, scrollEnabled, onNewWalletPress } = props;
const renderItem = useCallback(
({ item, index }: ListRenderItemInfo<TWallet>) =>
item ? (
@ -357,8 +356,8 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
}, []);
const onScrollToIndexFailed = (error: { averageItemLength: number; index: number }): void => {
console.log('onScrollToIndexFailed');
console.log(error);
console.debug('onScrollToIndexFailed');
console.debug(error);
flatListRef.current?.scrollToOffset({ offset: error.averageItemLength * error.index, animated: true });
setTimeout(() => {
if (data.length !== 0 && flatListRef.current !== null) {
@ -390,7 +389,7 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
ListHeaderComponent={ListHeaderComponent}
style={{ minHeight: sliderHeight + 12 }}
onScrollToIndexFailed={onScrollToIndexFailed}
ListFooterComponent={showNewWalletPanel && onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
ListFooterComponent={onNewWalletPress ? <NewWalletPanel onPress={onNewWalletPress} /> : null}
{...props}
/>
) : (
@ -406,7 +405,7 @@ const WalletsCarousel = forwardRef<FlatListRefType, WalletsCarouselProps>((props
/>
) : null,
)}
{showNewWalletPanel && onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
{onNewWalletPress && <NewWalletPanel onPress={onNewWalletPress} />}
</View>
);
});

View file

@ -10,53 +10,42 @@ import { navigationRef } from '../NavigationService';
* @param showFileImportButton {boolean}
*
* @param onDismiss {function} - if camera is closed via X button it gets triggered
* @param options {object} - additional options to pass to navigate
* @return {Promise<string | null>}
* @param useMerge {boolean} - if true, will merge the new screen with the current screen, otherwise will replace the current screen
* @return {Promise<string>}
*/
function scanQrHelper(
currentScreenName: string,
showFileImportButton = true,
onDismiss?: () => void,
options: { merge: boolean } = { merge: true },
useMerge = true,
): Promise<string | null> {
return requestCameraAuthorization().then(() => {
return new Promise(resolve => {
const params: any = {
showFileImportButton: Boolean(showFileImportButton),
};
let params = {};
if (options?.merge) {
if (onDismiss) {
params.onDismiss = onDismiss;
}
params.onBarScanned = function (data: any) {
if (useMerge) {
const onBarScanned = function (data: any) {
setTimeout(() => resolve(data.data || data), 1);
navigationRef.navigate({
name: currentScreenName,
params: {},
merge: options?.merge,
});
navigationRef.navigate({ name: currentScreenName, params: {}, merge: true });
};
navigationRef.navigate({
name: 'ScanQRCodeRoot',
params: {
screen: 'ScanQRCode',
params,
},
merge: true,
});
params = {
showFileImportButton: Boolean(showFileImportButton),
onDismiss,
onBarScanned,
};
} else {
navigationRef.navigate({
name: 'ScanQRCodeRoot',
params: {
screen: 'ScanQRCode',
params: {
showFileImportButton: Boolean(showFileImportButton),
},
},
});
params = { launchedBy: currentScreenName, showFileImportButton: Boolean(showFileImportButton) };
}
navigationRef.navigate({
name: 'ScanQRCodeRoot',
params: {
screen: 'ScanQRCode',
params,
},
merge: true,
});
});
});
}

View file

@ -1,42 +0,0 @@
{
"originHash" : "52530e6b1e3a85c8854952ef703a6d1bbe1acd82713be2b3166476b9b277db23",
"pins" : [
{
"identity" : "bugsnag-cocoa",
"kind" : "remoteSourceControl",
"location" : "https://github.com/bugsnag/bugsnag-cocoa",
"state" : {
"revision" : "16b9145fc66e5296f16e733f6feb5d0e450574e8",
"version" : "6.28.1"
}
},
{
"identity" : "efqrcode",
"kind" : "remoteSourceControl",
"location" : "https://github.com/EFPrefix/EFQRCode.git",
"state" : {
"revision" : "2991c2f318ad9529d93b2a73a382a3f9c72c64ce",
"version" : "6.2.2"
}
},
{
"identity" : "keychain-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/evgenyneu/keychain-swift.git",
"state" : {
"revision" : "5e1b02b6a9dac2a759a1d5dbc175c86bd192a608",
"version" : "24.0.0"
}
},
{
"identity" : "swift_qrcodejs",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ApolloZhu/swift_qrcodejs.git",
"state" : {
"revision" : "374dc7f7b9e76c6aeb393f6a84590c6d387e1ecb",
"version" : "2.2.2"
}
}
],
"version" : 3
}

View file

@ -865,4 +865,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: f19eea438501edfe85fb2fa51d40ba1b57540758
COCOAPODS: 1.15.2
COCOAPODS: 1.14.3

View file

@ -3,7 +3,7 @@ import { SendDetailsParams } from './SendDetailsStackParamList';
export type DetailViewStackParamList = {
UnlockWithScreen: undefined;
WalletsList: undefined;
WalletsList: { scannedData?: string };
WalletTransactions: { walletID: string; walletType: string };
LDKOpenChannelRoot: undefined;
LdkInfo: undefined;
@ -18,7 +18,7 @@ export type DetailViewStackParamList = {
LNDViewInvoice: { invoice: LightningTransaction; walletID: string };
LNDViewAdditionalInvoiceInformation: { invoiceId: string };
LNDViewAdditionalInvoicePreImage: { invoiceId: string };
Broadcast: undefined;
Broadcast: { scannedData?: string };
IsItMyAddress: undefined;
GenerateWord: undefined;
LnurlPay: undefined;

11
package-lock.json generated
View file

@ -24,7 +24,7 @@
"@react-native/metro-config": "0.74.84",
"@react-navigation/drawer": "6.7.2",
"@react-navigation/native": "6.1.18",
"@react-navigation/native-stack": "6.10.1",
"@react-navigation/native-stack": "6.11.0",
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
"@rneui/base": "4.0.0-rc.8",
"@rneui/themed": "4.0.0-rc.8",
@ -70,7 +70,7 @@
"react-native-default-preference": "1.4.4",
"react-native-device-info": "11.1.0",
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#6033c4e1b0dd0a6760b5f5a5a2c3b2e5d07f2ae4",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3061e30",
"react-native-fs": "2.20.0",
"react-native-gesture-handler": "2.17.1",
"react-native-handoff": "https://github.com/BlueWallet/react-native-handoff#31d005f93d31099d0e564590a3bbd052b8a02b39",
@ -139,6 +139,7 @@
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-native": "^4.1.0",
"jest": "^29.4.2",
"jest-environment-node": "^29.7.0",
"node-fetch": "^2.6.7",
"prettier": "^3.2.5",
"react-test-renderer": "18.2.0",
@ -6235,9 +6236,9 @@
}
},
"node_modules/@react-navigation/native-stack": {
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.10.1.tgz",
"integrity": "sha512-MDTHtelO2EhY1sEbz4YMNw+1YoE0sWXSHLUYireQZFHUXKDfYiWYbxXg2Q08Ool0KEvnZjP0hh0Wm6/ZnvDCtw==",
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.11.0.tgz",
"integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==",
"dependencies": {
"@react-navigation/elements": "^1.3.31",
"warn-once": "^0.1.0"

View file

@ -35,6 +35,7 @@
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-native": "^4.1.0",
"jest": "^29.4.2",
"jest-environment-node": "^29.7.0",
"node-fetch": "^2.6.7",
"prettier": "^3.2.5",
"react-test-renderer": "18.2.0",
@ -57,7 +58,7 @@
"postinstall": "rn-nodeify --install buffer,events,process,stream,inherits,path,assert,crypto --hack; npm run releasenotes2json; npm run branch2json; npm run patches",
"patches": "patch -p1 < scripts/rn-ldk.patch; patch -p1 < scripts/react-native-camera-kit.patch;",
"test": "npm run tslint && npm run lint && npm run unit && npm run jest",
"jest": "jest -b tests/integration/*",
"jest": "jest tests/integration/*",
"e2e:debug-build": "detox build -c android.debug",
"e2e:debug-test": "detox test -c android.debug -d 200000 -l info",
"e2e:debug": "(test -f android/app/build/outputs/apk/debug/app-debug.apk && test -f android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk) || npm run e2e:debug-build; npm run e2e:debug-test",
@ -67,9 +68,17 @@
"lint": " npm run tslint && node scripts/find-unused-loc.js && eslint --ext .js,.ts,.tsx '*.@(js|ts|tsx)' screen 'blue_modules/*.@(js|ts|tsx)' class models loc tests components navigation typings",
"lint:fix": "npm run lint -- --fix",
"lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep -E '\\.js|\\.ts' --color=never | awk '{print $2}' | xargs eslint --fix; exit 0",
"unit": "jest -b -i tests/unit/*"
"unit": "jest -b -w tests/unit/*"
},
"jest": {
"testEnvironment": "<rootDir>/tests/custom-environment.js",
"reporters": [
"default",
[
"<rootDir>/tests/custom-reporter.js",
{}
]
],
"preset": "react-native",
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
@ -108,7 +117,7 @@
"@react-native/metro-config": "0.74.84",
"@react-navigation/drawer": "6.7.2",
"@react-navigation/native": "6.1.18",
"@react-navigation/native-stack": "6.10.1",
"@react-navigation/native-stack": "6.11.0",
"@remobile/react-native-qrcode-local-image": "https://github.com/BlueWallet/react-native-qrcode-local-image",
"@rneui/base": "4.0.0-rc.8",
"@rneui/themed": "4.0.0-rc.8",
@ -154,7 +163,7 @@
"react-native-default-preference": "1.4.4",
"react-native-device-info": "11.1.0",
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#6033c4e1b0dd0a6760b5f5a5a2c3b2e5d07f2ae4",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#3061e30",
"react-native-fs": "2.20.0",
"react-native-gesture-handler": "2.17.1",
"react-native-handoff": "https://github.com/BlueWallet/react-native-handoff#31d005f93d31099d0e564590a3bbd052b8a02b39",
@ -184,7 +193,7 @@
"react-native-vector-icons": "10.1.0",
"react-native-watch-connectivity": "1.1.0",
"readable-stream": "3.6.2",
"realm": "12.11.1",
"realm": "12.12.1",
"rn-ldk": "github:BlueWallet/rn-ldk#v0.8.4",
"rn-nodeify": "10.3.0",
"scryptsy": "2.1.0",

View file

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { useRoute } from '@react-navigation/native';
import React, { useState, useEffect } from 'react';
import { useRoute, RouteProp } from '@react-navigation/native';
import * as bitcoin from 'bitcoinjs-lib';
import { ActivityIndicator, Keyboard, KeyboardAvoidingView, Linking, Platform, StyleSheet, TextInput, View } from 'react-native';
@ -23,6 +23,7 @@ import SafeArea from '../../components/SafeArea';
import { useTheme } from '../../components/themes';
import { scanQrHelper } from '../../helpers/scan-qr';
import loc from '../../loc';
import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList';
const BROADCAST_RESULT = Object.freeze({
none: 'Input transaction hex',
@ -31,12 +32,10 @@ const BROADCAST_RESULT = Object.freeze({
error: 'error',
});
interface SuccessScreenProps {
tx: string;
}
type RouteProps = RouteProp<DetailViewStackParamList, 'Broadcast'>;
const Broadcast: React.FC = () => {
const { name } = useRoute();
const { name, params } = useRoute<RouteProps>();
const [tx, setTx] = useState<string | undefined>();
const [txHex, setTxHex] = useState<string | undefined>();
const { colors } = useTheme();
@ -50,6 +49,14 @@ const Broadcast: React.FC = () => {
},
});
useEffect(() => {
const scannedData = params?.scannedData;
if (scannedData) {
handleScannedData(scannedData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [params?.scannedData]);
const handleUpdateTxHex = (nextValue: string) => setTxHex(nextValue.trim());
const handleBroadcast = async () => {
@ -81,10 +88,7 @@ const Broadcast: React.FC = () => {
}
};
const handleQRScan = async () => {
const scannedData = await scanQrHelper(name);
if (!scannedData) return;
const handleScannedData = (scannedData: string) => {
if (scannedData.indexOf('+') === -1 && scannedData.indexOf('=') === -1 && scannedData.indexOf('=') === -1) {
// this looks like NOT base64, so maybe its transaction's hex
return handleUpdateTxHex(scannedData);
@ -97,6 +101,10 @@ const Broadcast: React.FC = () => {
} catch (e) {}
};
const handleQRScan = () => {
scanQrHelper(name, true, undefined, false);
};
let status;
switch (broadcastResult) {
case BROADCAST_RESULT.none:
@ -159,7 +167,7 @@ const Broadcast: React.FC = () => {
);
};
const SuccessScreen: React.FC<SuccessScreenProps> = ({ tx }) => {
const SuccessScreen: React.FC<{ tx: string }> = ({ tx }) => {
if (!tx) {
return null;
}

View file

@ -127,9 +127,13 @@ const ScanQRCode = () => {
const data = decoder.toString();
decoder = false; // nullify for future use (?)
if (launchedBy) {
navigation.navigate({ name: launchedBy, params: {}, merge: true });
let merge = true;
if (typeof onBarScanned !== 'function') {
merge = false;
}
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
}
onBarScanned({ data });
onBarScanned && onBarScanned({ data });
} else {
setUrTotal(100);
setUrHave(Math.floor(decoder.estimatedPercentComplete() * 100));
@ -176,9 +180,13 @@ const ScanQRCode = () => {
data = Buffer.from(payload, 'hex').toString();
}
if (launchedBy) {
navigation.navigate({ name: launchedBy, params: {}, merge: true });
let merge = true;
if (typeof onBarScanned !== 'function') {
merge = false;
}
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
}
onBarScanned({ data });
onBarScanned && onBarScanned({ data });
} else {
setAnimatedQRCodeData(animatedQRCodeData);
}
@ -237,11 +245,15 @@ const ScanQRCode = () => {
try {
const hex = Base43.decode(ret.data);
bitcoin.Psbt.fromHex(hex); // if it doesnt throw - all good
const data = Buffer.from(hex, 'hex').toString('base64');
if (launchedBy) {
navigation.navigate({ name: launchedBy, params: {}, merge: true });
let merge = true;
if (typeof onBarScanned !== 'function') {
merge = false;
}
navigation.navigate({ name: launchedBy, params: { scannedData: data }, merge });
}
onBarScanned({ data: Buffer.from(hex, 'hex').toString('base64') });
onBarScanned && onBarScanned({ data });
return;
} catch (_) {}
@ -249,9 +261,13 @@ const ScanQRCode = () => {
setIsLoading(true);
try {
if (launchedBy) {
navigation.navigate({ name: launchedBy, params: {}, merge: true });
let merge = true;
if (typeof onBarScanned !== 'function') {
merge = false;
}
navigation.navigate({ name: launchedBy, params: { scannedData: ret.data }, merge });
}
onBarScanned(ret.data);
onBarScanned && onBarScanned(ret.data);
} catch (e) {
console.log(e);
}
@ -304,7 +320,11 @@ const ScanQRCode = () => {
const dismiss = () => {
if (launchedBy) {
navigation.navigate({ name: launchedBy, params: {}, merge: true });
let merge = true;
if (typeof onBarScanned !== 'function') {
merge = false;
}
navigation.navigate({ name: launchedBy, params: {}, merge });
} else {
navigation.goBack();
}

View file

@ -328,8 +328,8 @@ const CoinControl = () => {
const handleChoose = item => setOutput(item);
const handleUseCoin = u => {
bottomModalRef.current?.dismiss();
const handleUseCoin = async u => {
await bottomModalRef.current?.dismiss();
setOutput(null);
navigation.pop();
onUTXOChoose(u);
@ -395,8 +395,6 @@ const CoinControl = () => {
useEffect(() => {
if (output) {
bottomModalRef.current?.present();
} else {
bottomModalRef.current?.dismiss();
}
}, [output]);
@ -428,6 +426,7 @@ const CoinControl = () => {
<Button testID="UseCoin" title={loc.cc.use_coin} onPress={() => handleUseCoin([output])} />
</View>
}
footerDefaultMargins
contentContainerStyle={[styles.modalContent, styles.modalMinHeight]}
>
{output && renderOutputModalContent()}
@ -472,9 +471,8 @@ const styles = StyleSheet.create({
},
modalContent: {
padding: 22,
paddingTop: 66,
},
modalMinHeight: Platform.OS === 'android' ? { minHeight: 420 } : {},
modalMinHeight: Platform.OS === 'android' ? { minHeight: 500 } : {},
empty: {
flex: 1,
justifyContent: 'center',

View file

@ -145,7 +145,6 @@ const DrawerList: React.FC<DrawerListProps> = memo(({ navigation }) => {
<Header leftText={loc.wallets.list_title} onNewWalletPress={onNewWalletPress} isDrawerList />
<WalletsCarousel
data={state.wallets}
showNewWalletPanel
extraData={[state.wallets]}
onPress={handleClick}
handleLongPress={handleLongPress}

View file

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useReducer, useRef } from 'react';
import { useFocusEffect, useIsFocused, useRoute } from '@react-navigation/native';
import { useFocusEffect, useIsFocused, useRoute, RouteProp } from '@react-navigation/native';
import { findNodeHandle, Image, InteractionManager, SectionList, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import A from '../../blue_modules/analytics';
import BlueClipboard from '../../blue_modules/clipboard';
@ -88,6 +88,7 @@ function reducer(state: WalletListState, action: WalletListAction) {
}
type NavigationProps = NativeStackNavigationProp<DetailViewStackParamList, 'WalletsList'>;
type RouteProps = RouteProp<DetailViewStackParamList, 'WalletsList'>;
const WalletsList: React.FC = () => {
const [state, dispatch] = useReducer<React.Reducer<WalletListState, WalletListAction>>(reducer, initialState);
@ -108,7 +109,8 @@ const WalletsList: React.FC = () => {
const { colors, scanImage } = useTheme();
const { navigate } = useExtendedNavigation<NavigationProps>();
const isFocused = useIsFocused();
const routeName = useRoute().name;
const route = useRoute<RouteProps>();
const routeName = route.name;
const dataSource = getTransactions(undefined, 10);
const walletsCount = useRef<number>(wallets.length);
const walletActionButtonsRef = useRef<any>();
@ -149,6 +151,14 @@ const WalletsList: React.FC = () => {
walletsCount.current = wallets.length;
}, [wallets]);
useEffect(() => {
const scannedData = route.params?.scannedData;
if (scannedData) {
onBarScanned(scannedData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [route.params?.scannedData]);
const verifyBalance = useCallback(() => {
if (getBalance() !== 0) {
A(A.ENUM.GOT_NONZERO_BALANCE);
@ -253,7 +263,6 @@ const WalletsList: React.FC = () => {
handleLongPress={handleLongPress}
onMomentumScrollEnd={onSnapToItem}
ref={walletsCarousel}
showNewWalletPanel
onNewWalletPress={handleClick}
testID="WalletsList"
horizontal
@ -332,9 +341,8 @@ const WalletsList: React.FC = () => {
};
const onScanButtonPressed = useCallback(() => {
scanQrHelper(routeName).then(onBarScanned);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
scanQrHelper(routeName, true, undefined, false);
}, [routeName]);
const onBarScanned = useCallback(
(value: any) => {
@ -381,7 +389,7 @@ const WalletsList: React.FC = () => {
});
break;
case 2:
scanQrHelper(routeName, true).then(data => onBarScanned(data));
scanQrHelper(routeName, true, undefined, false);
break;
case 3:
if (!isClipboardEmpty) {

View file

@ -180,7 +180,6 @@ const WalletsAddMultisigStep2 = () => {
await saveToDisk();
A(A.ENUM.CREATED_WALLET);
triggerHapticFeedback(HapticFeedbackTypes.NotificationSuccess);
await dismissAllModals();
navigateToWalletsList();
};

View file

@ -24,6 +24,7 @@ const WalletsImport = () => {
const route = useRoute();
const label = route?.params?.label ?? '';
const triggerImport = route?.params?.triggerImport ?? false;
const scannedData = route?.params?.scannedData ?? '';
const { isAdvancedModeEnabled } = useSettings();
const [importText, setImportText] = useState(label);
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
@ -76,7 +77,14 @@ const WalletsImport = () => {
useEffect(() => {
if (triggerImport) importButtonPressed();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [triggerImport]);
useEffect(() => {
if (scannedData) {
onBarScanned(scannedData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [scannedData]);
const importButtonPressed = () => {
const textToImport = onBlur();
@ -102,7 +110,6 @@ const WalletsImport = () => {
screen: 'ScanQRCode',
params: {
launchedBy: route.name,
onBarScanned,
showFileImportButton: true,
},
}),

View file

@ -0,0 +1,17 @@
import NodeEnvironment from 'jest-environment-node';
class CustomEnvironment extends NodeEnvironment {
async handleTestEvent(event) {
if (event.name === 'test_start') {
if (!process.env.RETRY) return;
const fullName = (event.test.parent.name === 'ROOT_DESCRIBE_BLOCK' ? '' : event.test.parent.name + ' ') + event.test.name;
const hash = require('crypto').createHash('md5').update(fullName).digest('hex');
if (require('fs').existsSync(`/tmp/${hash}`)) {
event.test.mode = 'skip';
console.log('skipping as it previously passed on CI:', fullName);
}
}
}
}
module.exports = CustomEnvironment;

34
tests/custom-reporter.js Normal file
View file

@ -0,0 +1,34 @@
/**
* @fileOverview to combat flakiness of jest integration tests we implement a diy retry mechanism:
* a custom reporter writes a lock file in /tmp for each successfull testcase.
* then when a test suite is restarted, a custom environment checks if a testcase passed previously and
* forcefully skips such test cases.
*/
class CustomReporter {
constructor(globalConfig, reporterOptions, reporterContext) {
this._globalConfig = globalConfig;
this._options = reporterOptions;
this._context = reporterContext;
}
onTestCaseResult(test, testCaseResult) {
if (!process.env.RETRY) return;
// since we cant distinguish several testcases in `it.each(...)`, we just ignore them so they will always run
if (testCaseResult.fullName.includes('can fetch balance, transactions & utxo, disableBatching=')) return;
if (testCaseResult.fullName.includes('BlueElectrum can do multiGetBalanceByAddress(), disableBatching=')) return;
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetHistoryByAddress(), disableBatching=')) return;
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetTransactionByTxid(), disableBatching=')) return;
if (testCaseResult.fullName.includes('ElectrumClient can do multiGetHistoryByAddress() to obtain txhex, disableBatching=')) return;
if (testCaseResult.fullName.includes('addresses for vout missing')) return;
if (testCaseResult.fullName.includes('txdatas were coming back null from BlueElectrum because of high batchsize')) return;
const hash = require('crypto').createHash('md5').update(testCaseResult.fullName).digest('hex');
if (testCaseResult.status === 'passed') {
// marking testcase as passed in /tmp
require('fs').writeFileSync(`/tmp/${hash}`, '1');
}
}
}
module.exports = CustomReporter;

View file

@ -16,19 +16,13 @@ import {
/**
* this testsuite is for test cases that require no wallets to be present
*/
beforeAll(async () => {
// reinstalling the app just for any case to clean up app's storage
await device.launchApp({ delete: true });
}, 300_000);
describe('BlueWallet UI Tests - no wallets', () => {
it('selftest passes', async () => {
const lockFile = '/tmp/travislock.' + hashIt('t1');
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t1'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await waitFor(element(by.id('WalletsList')))
.toBeVisible()
.withTimeout(300 * 1000);
@ -52,7 +46,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t2'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
// go to settings, press SelfTest and wait for OK
@ -190,7 +184,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t3'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
await helperCreateWallet();
@ -230,7 +224,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t4'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
// lets create a wallet
@ -384,7 +378,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t5'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
await helperCreateWallet();
await element(by.id('SettingsButton')).tap();
@ -461,7 +455,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await helperSwitchAdvancedMode();
await yo('WalletsList');
await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit
@ -541,7 +535,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t6'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit
await sleep(200); // Wait until bounce animation finishes.
@ -583,6 +577,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
await element(by.id('AddressInput')).replaceText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
await element(by.id('BitcoinAmountInput')).typeText('0.0005\n');
await element(by.id('BitcoinAmountInput')).tapReturnKey();
// setting fee rate:
const feeRate = 3;
@ -672,7 +667,7 @@ describe('BlueWallet UI Tests - no wallets', () => {
if (process.env.TRAVIS) {
if (require('fs').existsSync(lockFile)) return console.warn('skipping', JSON.stringify('t6'), 'as it previously passed on Travis');
}
await device.launchApp({ newInstance: true });
await device.launchApp({ delete: true }); // reinstalling the app just for any case to clean up app's storage
await yo('WalletsList');
// enable AdvancedMode to see derivation path in wallet details

View file

@ -3,12 +3,13 @@ import * as bitcoin from 'bitcoinjs-lib';
import { extractTextFromElementById, hashIt, helperImportWallet, sleep, sup, yo } from './helperz';
let importedSuccessfully = false;
/**
* in this suite each test requires that there is one specific wallet present, thus, we import it
* before anything else.
* we dont clean it up as we expect other test suites to do clean install of the app
*/
beforeAll(async () => {
if (!process.env.HD_MNEMONIC_BIP84) {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
@ -20,6 +21,7 @@ beforeAll(async () => {
console.log('before all - importing bip48...');
await helperImportWallet(process.env.HD_MNEMONIC_BIP84, 'HDsegwitBech32', 'Imported HD SegWit (BIP84 Bech32 Native)', '0.00105526');
console.log('...imported!');
importedSuccessfully = true;
await device.pressBack();
await sleep(15000);
}, 1200_000);
@ -34,6 +36,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
@ -183,6 +187,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
@ -256,6 +262,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
@ -325,6 +333,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
@ -368,6 +378,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
@ -417,6 +429,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
await device.launchApp({
@ -453,6 +467,8 @@ describe('BlueWallet UI Tests - import BIP84 wallet', () => {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
if (!importedSuccessfully) throw new Error('BIP84 was not imported during the setup');
await device.launchApp({ newInstance: true });
// go inside the wallet
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();