Browse Source

TST: detox e2e test to import BIP84 mnemonic & create real transaction

master
Overtorment 5 years ago
parent
commit
70af003a14
  1. 5
      BlueComponents.js
  2. 2
      screen/send/confirm.js
  3. 1
      screen/send/create.js
  4. 32
      screen/send/details.js
  5. 1
      screen/wallets/add.js
  6. 2
      screen/wallets/import.js
  7. 81
      tests/e2e/bluewallet.spec.js

5
BlueComponents.js

@ -313,6 +313,7 @@ export class BlueWalletNavigationHeader extends Component {
<BluePrivateBalance />
) : (
<Text
testID={'WalletBalance'}
numberOfLines={1}
adjustsFontSizeToFit
style={{
@ -1256,7 +1257,7 @@ export class BlueReceiveButtonIcon extends Component {
export class BlueSendButtonIcon extends Component {
render() {
return (
<TouchableOpacity {...this.props}>
<TouchableOpacity {...this.props} testID={'SendButton'}>
<View
style={{
flex: 1,
@ -2075,6 +2076,7 @@ export class BlueAddressInput extends Component {
}}
>
<TextInput
testID={'AddressInput'}
onChangeText={text => {
this.props.onChangeText(text);
}}
@ -2268,6 +2270,7 @@ export class BlueBitcoinAmount extends Component {
<View style={{ flexDirection: 'row', justifyContent: 'center', paddingTop: 16, paddingBottom: 2 }}>
<TextInput
{...this.props}
testID={'BitcoinAmountInput'}
keyboardType="numeric"
onChangeText={text => {
text = text.trim();

2
screen/send/confirm.js

@ -121,6 +121,7 @@ export default class Confirm extends Component {
<>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<Text
testID={'TransactionValue'}
style={{
color: '#0f5cc0',
fontSize: 36,
@ -197,6 +198,7 @@ export default class Confirm extends Component {
)}
<TouchableOpacity
testID={'TransactionDetailsButton'}
style={{ marginVertical: 24 }}
onPress={async () => {
if (this.isBiometricUseCapableAndEnabled) {

1
screen/send/create.js

@ -131,6 +131,7 @@ export default class SendCreate extends Component {
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
<BlueText style={{ color: '#0c2550', fontWeight: '500' }}>{loc.send.create.this_is_hex}</BlueText>
<TextInput
testID={'TxhexInput'}
style={{
borderColor: '#ebebeb',
backgroundColor: '#d2f8d6',

32
screen/send/details.js

@ -309,32 +309,6 @@ export default class SendDetails extends Component {
return (availableBalance === 'NaN' && balance) || availableBalance;
}
calculateFee(utxos, txhex, utxoIsInSatoshis) {
let index = {};
let c = 1;
index[0] = 0;
for (let utxo of utxos) {
if (!utxoIsInSatoshis) {
utxo.amount = new BigNumber(utxo.amount).multipliedBy(100000000).toNumber();
}
index[c] = utxo.amount + index[c - 1];
c++;
}
let tx = bitcoin.Transaction.fromHex(txhex);
let totalInput = index[tx.ins.length];
// ^^^ dumb way to calculate total input. we assume that signer uses utxos sequentially
// so total input == sum of yongest used inputs (and num of used inputs is `tx.ins.length`)
// TODO: good candidate to refactor and move to appropriate class. some day
let totalOutput = 0;
for (let o of tx.outs) {
totalOutput += o.value * 1;
}
return new BigNumber(totalInput - totalOutput).dividedBy(100000000).toNumber();
}
async processBIP70Invoice(text) {
try {
if (BitcoinBIP70TransactionDecode.matchesPaymentURL(text)) {
@ -760,7 +734,11 @@ export default class SendDetails extends Component {
renderCreateButton = () => {
return (
<View style={{ marginHorizontal: 56, marginVertical: 16, alignContent: 'center', backgroundColor: '#FFFFFF', minHeight: 44 }}>
{this.state.isLoading ? <ActivityIndicator /> : <BlueButton onPress={() => this.createTransaction()} title={'Next'} />}
{this.state.isLoading ? (
<ActivityIndicator />
) : (
<BlueButton onPress={() => this.createTransaction()} title={'Next'} testID={'CreateTransactionButton'} />
)}
</View>
);
};

1
screen/wallets/add.js

@ -329,6 +329,7 @@ export default class WalletsAdd extends Component {
)}
</View>
<BlueButtonLink
testID="ImportWallet"
style={{ marginBottom: 0, marginTop: 24 }}
title={loc.wallets.add.import_wallet}
onPress={() => {

2
screen/wallets/import.js

@ -71,6 +71,7 @@ const WalletsImport = () => {
<BlueFormLabel>{loc.wallets.import.explanation}</BlueFormLabel>
<BlueSpacing20 />
<BlueFormMultiInput
testID="MnemonicInput"
value={importText}
contextMenuHidden
onChangeText={setImportText}
@ -80,6 +81,7 @@ const WalletsImport = () => {
<BlueSpacing20 />
<View style={{ flex: 1, alignItems: 'center' }}>
<BlueButton
testID="DoImport"
disabled={importText.trim().length === 0}
title={loc.wallets.import.do_import}
buttonStyle={{

81
tests/e2e/bluewallet.spec.js

@ -1,5 +1,8 @@
/* global it, describe, expect, element, by, waitFor, device */
const bitcoin = require('bitcoinjs-lib');
const assert = require('assert');
describe('BlueWallet UI Tests', () => {
it('selftest passes', async () => {
await waitFor(element(by.id('WalletsList')))
@ -300,6 +303,78 @@ describe('BlueWallet UI Tests', () => {
await yo('WalletsList');
await expect(element(by.id('cr34t3d'))).toBeVisible();
});
it('can import BIP84 mnemonic, fetch balance & transactions, then create a transaction', async () => {
if (!process.env.HD_MNEMONIC_BIP84) {
console.error('process.env.HD_MNEMONIC_BIP84 not set, skipped');
return;
}
await yo('WalletsList');
// going to Import Wallet screen and importing mnemonic for existing BIP84 wallet with real balance
await element(by.id('CreateAWallet')).tap();
await element(by.id('ImportWallet')).tap();
await element(by.id('MnemonicInput')).typeText(process.env.HD_MNEMONIC_BIP84);
try {
await element(by.id('DoImport')).tap();
} catch (_) {}
await sleep(60000);
await sup('OK', 3 * 61000); // waiting for wallet import
await element(by.text('OK')).tap();
// ok, wallet imported
// lets go inside wallet
await element(by.text('Imported HD SegWit (BIP84 Bech32 Native)')).tap();
// label might change in the future; see HDSegwitBech32Wallet.typeReadable
expect(element(by.id('WalletBalance'))).toHaveText('0.00105526 BTC');
// lets create real transaction:
await element(by.id('SendButton')).tap();
await element(by.id('AddressInput')).typeText('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl');
await element(by.id('BitcoinAmountInput')).typeText('0.0005\n');
await sleep(5000);
try {
await element(by.id('CreateTransactionButton')).tap();
} catch (_) {}
// created. verifying:
await yo('TransactionValue');
expect(element(by.id('TransactionValue'))).toHaveText('0.0005');
await element(by.id('TransactionDetailsButton')).tap();
// now, a hack to extract element text. warning, this might break in future
// @see https://github.com/wix/detox/issues/445
let txhex = '';
try {
await expect(element(by.id('TxhexInput'))).toHaveText('_unfoundable_text');
} catch (error) {
if (device.getPlatform() === 'ios') {
const start = `accessibilityLabel was "`;
const end = '" on ';
const errorMessage = error.message.toString();
const [, restMessage] = errorMessage.split(start);
const [label] = restMessage.split(end);
txhex = label;
} else {
const start = 'Got:';
const end = '}"';
const errorMessage = error.message.toString();
const [, restMessage] = errorMessage.split(start);
const [label] = restMessage.split(end);
const value = label.split(',');
var combineText = value.find(i => i.includes('text=')).trim();
const [, elementText] = combineText.split('=');
txhex = elementText;
}
}
let transaction = bitcoin.Transaction.fromHex(txhex);
assert.strictEqual(transaction.ins.length, 2);
assert.strictEqual(transaction.outs.length, 2);
assert.strictEqual(bitcoin.address.fromOutputScript(transaction.outs[0].script), 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl'); // to address
assert.strictEqual(transaction.outs[0].value, 50000);
});
});
async function sleep(ms) {
@ -312,6 +387,12 @@ async function yo(id, timeout = 33000) {
.withTimeout(timeout);
}
async function sup(text, timeout = 33000) {
return waitFor(element(by.text(text)))
.toBeVisible()
.withTimeout(timeout);
}
async function helperCreateWallet(walletName) {
await element(by.id('CreateAWallet')).tap();
await element(by.id('WalletNameInput')).typeText(walletName || 'cr34t3d');

Loading…
Cancel
Save