2020-04-27 10:23:56 +02:00
const bitcoin = require ( 'bitcoinjs-lib' ) ;
const assert = require ( 'assert' ) ;
2020-05-23 08:50:08 +02:00
const createHash = require ( 'create-hash' ) ;
jasmine . getEnv ( ) . addReporter ( {
specStarted : result => ( jasmine . currentTest = result ) ,
specDone : result => ( jasmine . currentTest = result ) ,
} ) ;
2020-04-27 10:23:56 +02:00
2020-11-04 17:24:39 +01:00
describe ( 'BlueWallet UI Tests' , ( ) => {
2020-04-20 14:29:37 +02:00
it ( 'selftest passes' , async ( ) => {
2020-05-23 08:50:08 +02:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-05-23 08:50:08 +02:00
}
2020-04-20 14:29:37 +02:00
await waitFor ( element ( by . id ( 'WalletsList' ) ) )
. toBeVisible ( )
. withTimeout ( 300 * 1000 ) ;
// go to settings, press SelfTest and wait for OK
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
await element ( by . id ( 'AboutButton' ) ) . tap ( ) ;
await element ( by . id ( 'AboutScrollView' ) ) . swipe ( 'up' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
await element ( by . id ( 'RunSelfTestButton' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'SelfTestOk' ) ) )
. toBeVisible ( )
. withTimeout ( 300 * 1000 ) ;
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-04-20 14:29:37 +02:00
} ) ;
2021-08-19 18:30:19 +02:00
it ( 'all settings screens work' , async ( ) => {
2021-03-01 11:20:01 +01:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
}
await yo ( 'WalletsList' ) ;
// go to settings, press SelfTest and wait for OK
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
// general
await element ( by . id ( 'GeneralSettings' ) ) . tap ( ) ;
2021-04-06 12:31:56 +02:00
// privacy
// trigger switches
await element ( by . id ( 'SettingsPrivacy' ) ) . tap ( ) ;
await element ( by . id ( 'ClipboardSwith' ) ) . tap ( ) ;
await element ( by . id ( 'ClipboardSwith' ) ) . tap ( ) ;
await element ( by . id ( 'QuickActionsSwith' ) ) . tap ( ) ;
await element ( by . id ( 'QuickActionsSwith' ) ) . tap ( ) ;
await device . pressBack ( ) ;
// enable AdvancedMode
2021-03-01 11:20:01 +01:00
await element ( by . id ( 'AdvancedMode' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-09-16 20:56:09 +02:00
// disable it:
await element ( by . id ( 'GeneralSettings' ) ) . tap ( ) ;
await element ( by . id ( 'AdvancedMode' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-03-01 11:20:01 +01:00
//
// currency
// change currency to ARS ($) and switch it back to USD ($)
await element ( by . id ( 'Currency' ) ) . tap ( ) ;
await element ( by . text ( 'ARS ($)' ) ) . tap ( ) ;
await expect ( element ( by . text ( 'Prices are obtained from Yadio' ) ) ) . toBeVisible ( ) ;
await element ( by . text ( 'USD ($)' ) ) . tap ( ) ;
await device . pressBack ( ) ;
// language
// change language to Chinese (ZH), test it and switch back to English
await element ( by . id ( 'Language' ) ) . tap ( ) ;
await element ( by . text ( 'Chinese (ZH)' ) ) . tap ( ) ;
await device . pressBack ( ) ;
await expect ( element ( by . text ( 'è¯è¨€' ) ) ) . toBeVisible ( ) ;
await element ( by . id ( 'Language' ) ) . tap ( ) ;
await element ( by . text ( 'English' ) ) . tap ( ) ;
await device . pressBack ( ) ;
// security
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
await device . pressBack ( ) ;
// network
await element ( by . id ( 'NetworkSettings' ) ) . tap ( ) ;
// network -> electrum server
// change electrum server to electrum.blockstream.info and revert it back
await element ( by . id ( 'ElectrumSettings' ) ) . tap ( ) ;
await element ( by . id ( 'HostInput' ) ) . replaceText ( 'electrum.blockstream.info\n' ) ;
await element ( by . id ( 'PortInput' ) ) . replaceText ( '50001\n' ) ;
await element ( by . id ( 'Save' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'ResetToDefault' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'HostInput' ) ) ) . toHaveText ( '' ) ;
await expect ( element ( by . id ( 'PortInput' ) ) ) . toHaveText ( '' ) ;
2021-07-05 05:05:04 +02:00
await expect ( element ( by . id ( 'SSLPortInput' ) ) ) . toHaveToggleValue ( false ) ;
2021-03-01 11:20:01 +01:00
await device . pressBack ( ) ;
// network -> lightning
// change URI and revert it back
await element ( by . id ( 'LightningSettings' ) ) . tap ( ) ;
await element ( by . id ( 'URIInput' ) ) . replaceText ( 'invalid\n' ) ;
await element ( by . id ( 'Save' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
2021-08-30 05:51:24 +02:00
await expect ( element ( by . text ( 'Invalid LNDHub URI' ) ) ) . toBeVisible ( ) ;
2021-03-01 11:20:01 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'URIInput' ) ) . replaceText ( 'https://lndhub.herokuapp.com\n' ) ;
await element ( by . id ( 'Save' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await expect ( element ( by . text ( 'Your changes have been saved successfully.' ) ) ) . toBeVisible ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'URIInput' ) ) . replaceText ( '\n' ) ;
await element ( by . id ( 'Save' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await expect ( element ( by . text ( 'Your changes have been saved successfully.' ) ) ) . toBeVisible ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-04-11 22:53:05 +02:00
// notifications
2021-03-04 08:06:07 +01:00
// turn on notifications if available
2021-04-13 14:26:37 +02:00
// console.warn('yo');
// await sleep(300000);
2021-03-04 08:06:07 +01:00
if ( await expectToBeVisible ( 'NotificationSettings' ) ) {
2021-04-13 14:26:37 +02:00
await element ( by . id ( 'NotificationSettings' ) ) . tap ( ) ;
2021-03-04 08:06:07 +01:00
await element ( by . id ( 'NotificationsSwitch' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'NotificationsSwitch' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-03-31 17:21:39 +02:00
await device . pressBack ( ) ;
2021-04-13 14:26:37 +02:00
} else {
await device . pressBack ( ) ;
2021-03-04 08:06:07 +01:00
}
2021-03-01 11:20:01 +01:00
2021-03-31 17:21:39 +02:00
// tools
await element ( by . id ( 'Tools' ) ) . tap ( ) ;
// tools -> broadcast
// try to broadcast wrong tx
await element ( by . id ( 'Broadcast' ) ) . tap ( ) ;
await element ( by . id ( 'TxHex' ) ) . replaceText ( 'invalid\n' ) ;
await element ( by . id ( 'BroadcastButton' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
// await expect(element(by.text('the transaction was rejected by network rules....'))).toBeVisible();
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-04-01 12:11:53 +02:00
// IsItMyAddress
await element ( by . id ( 'IsItMyAddress' ) ) . tap ( ) ;
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'CheckAddress' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'Result' ) ) ) . toHaveText ( 'None of the available wallets own the provided address.' ) ;
await device . pressBack ( ) ;
2021-03-31 18:38:43 +02:00
await device . pressBack ( ) ;
2021-03-01 11:20:01 +01:00
// about
await element ( by . id ( 'AboutButton' ) ) . tap ( ) ;
await device . pressBack ( ) ;
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
2021-03-01 11:20:01 +01:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
} ) ;
2021-01-24 01:40:39 +01:00
it ( 'can create wallet, reload app and it persists. then go to receive screen, set custom amount and label. Dismiss modal and go to WalletsList.' , async ( ) => {
2020-05-23 08:50:08 +02:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-05-23 08:50:08 +02:00
}
2020-04-30 14:47:10 +02:00
await yo ( 'WalletsList' ) ;
await helperCreateWallet ( ) ;
await device . launchApp ( { newInstance : true } ) ;
await yo ( 'WalletsList' ) ;
await expect ( element ( by . id ( 'cr34t3d' ) ) ) . toBeVisible ( ) ;
2021-01-23 05:50:52 +01:00
await element ( by . id ( 'cr34t3d' ) ) . tap ( ) ;
await element ( by . id ( 'ReceiveButton' ) ) . tap ( ) ;
await element ( by . text ( 'Yes, I have' ) ) . tap ( ) ;
2021-01-27 14:57:07 +01:00
try {
// in case emulator has no google services and doesnt support pushes
// we just dont show this popup
2021-02-09 14:35:04 +01:00
await element ( by . text ( ` No, and don’ t ask me again ` ) ) . tap ( ) ;
2021-01-27 14:57:07 +01:00
} catch ( _ ) { }
2021-01-23 05:50:52 +01:00
await yo ( 'BitcoinAddressQRCodeContainer' ) ;
await yo ( 'BlueCopyTextToClipboard' ) ;
await element ( by . id ( 'SetCustomAmountButton' ) ) . tap ( ) ;
2021-07-06 11:38:53 +02:00
await element ( by . id ( 'BitcoinAmountInput' ) ) . replaceText ( '1' ) ;
2021-01-23 05:50:52 +01:00
await element ( by . id ( 'CustomAmountDescription' ) ) . typeText ( 'test' ) ;
await element ( by . id ( 'CustomAmountSaveButton' ) ) . tap ( ) ;
await sup ( '1 BTC' ) ;
await sup ( 'test' ) ;
await yo ( 'BitcoinAddressQRCodeContainer' ) ;
await yo ( 'BlueCopyTextToClipboard' ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-09-16 20:56:09 +02:00
await helperDeleteWallet ( 'cr34t3d' ) ;
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-04-30 14:47:10 +02:00
} ) ;
2021-09-16 20:56:09 +02:00
it ( 'can encrypt storage, with plausible deniabilityl decrypt fake storage' , async ( ) => {
2020-05-23 08:50:08 +02:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-05-23 08:50:08 +02:00
}
2020-03-19 16:51:35 +01:00
await yo ( 'WalletsList' ) ;
// lets create a wallet
await helperCreateWallet ( ) ;
2020-03-18 16:38:52 +01:00
2020-03-19 16:51:35 +01:00
// go to settings
await expect ( element ( by . id ( 'SettingsButton' ) ) ) . toBeVisible ( ) ;
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
2020-03-20 16:25:23 +01:00
await expect ( element ( by . id ( 'SecurityButton' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
// go to Security page where we will enable encryption
2020-03-20 16:25:23 +01:00
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
2020-08-25 13:33:33 +02:00
// await expect(element(by.id('EncyptedAndPasswordProtected'))).toBeVisible(); // @see https://github.com/react-native-elements/react-native-elements/issues/2519
2020-03-19 16:51:35 +01:00
await expect ( element ( by . id ( 'PlausibleDeniabilityButton' ) ) ) . toBeNotVisible ( ) ;
2020-03-18 18:03:05 +01:00
if ( device . getPlatform ( ) === 'ios' ) {
console . warn ( 'Android only test skipped' ) ;
return ;
}
2020-03-19 16:51:35 +01:00
// lets encrypt the storage.
2020-03-18 18:03:05 +01:00
// first, trying to mistype second password:
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol. lets tap it
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( '08902' ) ;
2020-03-18 18:03:05 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( '666' ) ;
2020-03-18 18:03:05 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-12 22:14:57 +01:00
await expect ( element ( by . text ( 'Passwords do not match.' ) ) ) . toBeVisible ( ) ;
2020-03-18 18:03:05 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-03-19 16:51:35 +01:00
// now, lets put correct passwords and encrypt the storage
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'qqq' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'qqq' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-03-16 14:51:08 +01:00
2020-03-19 16:51:35 +01:00
// relaunch app
await device . launchApp ( { newInstance : true } ) ;
await waitFor ( element ( by . text ( 'OK' ) ) )
2020-03-18 16:38:52 +01:00
. toBeVisible ( )
2020-03-19 17:55:35 +01:00
. withTimeout ( 33000 ) ;
2020-03-18 16:38:52 +01:00
2020-11-22 10:18:05 +01:00
// trying to decrypt with incorrect password
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Your storage is encrypted. Password is required to decrypt it.' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'wrong' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Incorrect password. Please try again.' ) ) ) . toBeVisible ( ) ;
2020-03-18 16:38:52 +01:00
2020-03-19 16:51:35 +01:00
// correct password
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'qqq' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await yo ( 'WalletsList' ) ;
2020-03-18 16:38:52 +01:00
2020-03-19 16:51:35 +01:00
// previously created wallet should be visible
2020-03-18 16:38:52 +01:00
await expect ( element ( by . id ( 'cr34t3d' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
// now lets enable plausible deniability feature
// go to settings -> security screen -> plausible deniability screen
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
2020-03-20 16:25:23 +01:00
await expect ( element ( by . id ( 'SecurityButton' ) ) ) . toBeVisible ( ) ;
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
2020-08-25 13:33:33 +02:00
// await expect(element(by.id('EncyptedAndPasswordProtected'))).toBeVisible(); // @see https://github.com/react-native-elements/react-native-elements/issues/2519
2020-03-19 16:51:35 +01:00
await expect ( element ( by . id ( 'PlausibleDeniabilityButton' ) ) ) . toBeVisible ( ) ;
await element ( by . id ( 'PlausibleDeniabilityButton' ) ) . tap ( ) ;
// trying to enable plausible denability
await element ( by . id ( 'CreateFakeStorageButton' ) ) . tap ( ) ;
2020-12-12 22:14:57 +01:00
await expect ( element ( by . text ( 'Password for the fake storage should not match the password for your main storage.' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
// trying MAIN password: should fail, obviously
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'qqq' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Password is currently in use. Please try a different password.' ) ) ) . toBeVisible ( ) ;
2020-04-30 14:47:10 +02:00
if ( process . env . TRAVIS ) await sleep ( 3000 ) ; // hopefully helps prevent crash
2020-03-19 16:51:35 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-04-30 14:47:10 +02:00
if ( process . env . TRAVIS ) await sleep ( 3000 ) ; // hopefully helps prevent crash
2020-03-19 16:51:35 +01:00
// trying new password, but will mistype
await element ( by . id ( 'CreateFakeStorageButton' ) ) . tap ( ) ;
2020-04-30 14:47:10 +02:00
if ( process . env . TRAVIS ) await sleep ( 3000 ) ; // hopefully helps prevent crash
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorage' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Re-type password' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorageWithTypo' ) ; // retyping with typo
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Passwords do not match. Please try again.' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// trying new password
await element ( by . id ( 'CreateFakeStorageButton' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorage' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Re-type password' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorage' ) ; // retyping
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await expect ( element ( by . text ( 'Success' ) ) ) . toBeVisible ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// created fake storage.
// creating a wallet inside this fake storage
await helperCreateWallet ( 'fake_wallet' ) ;
// relaunch the app, unlock with fake password, expect to see fake wallet
// relaunch app
2020-03-18 16:38:52 +01:00
await device . launchApp ( { newInstance : true } ) ;
2020-03-19 16:51:35 +01:00
await waitFor ( element ( by . text ( 'OK' ) ) )
2020-03-18 16:38:52 +01:00
. toBeVisible ( )
2020-03-19 17:55:35 +01:00
. withTimeout ( 33000 ) ;
2020-03-19 16:51:35 +01:00
//
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Your storage is encrypted. Password is required to decrypt it.' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'qqq' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await yo ( 'WalletsList' ) ;
// previously created wallet IN MAIN STORAGE should be visible
await expect ( element ( by . id ( 'cr34t3d' ) ) ) . toBeVisible ( ) ;
// relaunch app
await device . launchApp ( { newInstance : true } ) ;
await sleep ( 3000 ) ;
//
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Your storage is encrypted. Password is required to decrypt it.' ) ) ) . toBeVisible ( ) ;
2020-03-19 16:51:35 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorage' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await yo ( 'WalletsList' ) ;
// previously created wallet in FAKE storage should be visible
await expect ( element ( by . id ( 'fake_wallet' ) ) ) . toBeVisible ( ) ;
2020-03-20 12:46:45 +01:00
2021-09-16 20:56:09 +02:00
// now derypting it, to cleanup
2020-03-20 12:46:45 +01:00
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
2020-03-20 16:25:23 +01:00
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
2020-03-20 12:46:45 +01:00
// correct password
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-16 20:56:09 +02:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'passwordForFakeStorage' ) ;
2020-03-20 12:46:45 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-16 20:56:09 +02:00
await helperDeleteWallet ( 'fake_wallet' ) ;
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-03-20 12:46:45 +01:00
} ) ;
2021-09-16 20:56:09 +02:00
it ( 'can encrypt storage, and decrypt storage works' , async ( ) => {
2020-05-23 08:50:08 +02:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-05-23 08:50:08 +02:00
}
2020-03-20 12:46:45 +01:00
await yo ( 'WalletsList' ) ;
await helperCreateWallet ( ) ;
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
2020-03-20 16:25:23 +01:00
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
2020-03-20 12:46:45 +01:00
if ( device . getPlatform ( ) === 'ios' ) {
console . warn ( 'Android only test skipped' ) ;
return ;
}
// lets encrypt the storage.
// lets put correct passwords and encrypt the storage
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'pass' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'pass' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'PlausibleDeniabilityButton' ) ) . tap ( ) ;
// trying to enable plausible denability
await element ( by . id ( 'CreateFakeStorageButton' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'fake' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Re-type password' ) ) ) . toBeVisible ( ) ;
2020-03-20 12:46:45 +01:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'fake' ) ; // retyping
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await expect ( element ( by . text ( 'Success' ) ) ) . toBeVisible ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// created fake storage.
// creating a wallet inside this fake storage
await helperCreateWallet ( 'fake_wallet' ) ;
// relaunch app
await device . launchApp ( { newInstance : true } ) ;
await waitFor ( element ( by . text ( 'OK' ) ) )
. toBeVisible ( )
. withTimeout ( 33000 ) ;
//
2020-12-09 21:47:46 +01:00
await expect ( element ( by . text ( 'Your storage is encrypted. Password is required to decrypt it.' ) ) ) . toBeVisible ( ) ;
2021-09-16 20:56:09 +02:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'pass' ) ;
2020-03-20 12:46:45 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await yo ( 'WalletsList' ) ;
2021-09-16 20:56:09 +02:00
// previously created wallet IN MAIN STORAGE should be visible
await expect ( element ( by . id ( 'cr34t3d' ) ) ) . toBeVisible ( ) ;
2020-03-20 12:46:45 +01:00
// now go to settings, and decrypt
await element ( by . id ( 'SettingsButton' ) ) . tap ( ) ;
2020-03-20 16:25:23 +01:00
await element ( by . id ( 'SecurityButton' ) ) . tap ( ) ;
2020-03-20 12:46:45 +01:00
2021-09-16 20:56:09 +02:00
// putting FAKE storage password. should not succeed
2020-03-20 12:46:45 +01:00
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-16 20:56:09 +02:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'fake' ) ;
2020-03-20 12:46:45 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-12-10 16:28:56 +01:00
await expect ( element ( by . text ( 'Incorrect password. Please try again.' ) ) ) . toBeVisible ( ) ;
2020-03-20 12:46:45 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// correct password
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // thats a switch lol
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-16 20:56:09 +02:00
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'pass' ) ;
2020-03-20 12:46:45 +01:00
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// relaunch app
await device . launchApp ( { newInstance : true } ) ;
2021-09-16 20:56:09 +02:00
await yo ( 'cr34t3d' ) ; // success
await helperDeleteWallet ( 'cr34t3d' ) ;
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-03-20 12:46:45 +01:00
} ) ;
2021-02-18 14:37:43 +01:00
it ( 'can import BIP84 mnemonic, fetch balance & transactions, then create a transaction; then cosign' , async ( ) => {
2020-05-23 08:50:08 +02:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-05-23 08:50:08 +02:00
}
2020-04-27 10:23:56 +02:00
if ( ! process . env . HD _MNEMONIC _BIP84 ) {
console . error ( 'process.env.HD_MNEMONIC_BIP84 not set, skipped' ) ;
return ;
}
2020-04-29 16:27:07 +02:00
await helperImportWallet ( process . env . HD _MNEMONIC _BIP84 , 'Imported HD SegWit (BIP84 Bech32 Native)' , '0.00105526 BTC' ) ;
2020-04-27 10:23:56 +02:00
// lets create real transaction:
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
2020-09-14 19:02:24 +02:00
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
2020-05-16 14:56:35 +02:00
await element ( by . id ( 'BitcoinAmountInput' ) ) . typeText ( '0.0001\n' ) ;
2020-09-15 16:02:10 +02:00
// setting fee rate:
const feeRate = 2 ;
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( feeRate + '' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2020-04-29 16:27:07 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
2020-04-27 10:23:56 +02:00
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionValue' ) ;
2020-05-16 14:56:35 +02:00
expect ( element ( by . id ( 'TransactionValue' ) ) ) . toHaveText ( '0.0001' ) ;
2021-03-19 16:29:27 +01:00
const transactionFee = await extractTextFromElementById ( 'TransactionFee' ) ;
2021-09-14 14:30:41 +02:00
assert . ok ( transactionFee . startsWith ( 'Fee: 0.00000292 BTC' ) , 'Unexpected tx fee: ' + transactionFee ) ;
2020-04-27 10:23:56 +02:00
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
2020-09-15 18:11:18 +02:00
let txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
2020-04-27 10:23:56 +02:00
2020-09-15 18:11:18 +02:00
let transaction = bitcoin . Transaction . fromHex ( txhex ) ;
2020-04-29 16:27:07 +02:00
assert . ok ( transaction . ins . length === 1 || transaction . ins . length === 2 ) ; // depending on current fees gona use either 1 or 2 inputs
2020-04-27 10:23:56 +02:00
assert . strictEqual ( transaction . outs . length , 2 ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 0 ] . script ) , 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ; // to address
2020-05-16 14:56:35 +02:00
assert . strictEqual ( transaction . outs [ 0 ] . value , 10000 ) ;
2020-09-15 16:02:10 +02:00
// checking fee rate:
2021-05-11 12:18:24 +02:00
const totalIns = 100000 ; // we hardcode it since we know it in advance
2020-09-15 16:02:10 +02:00
const totalOuts = transaction . outs . map ( el => el . value ) . reduce ( ( a , b ) => a + b , 0 ) ;
2021-09-14 14:30:41 +02:00
const tx = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( Math . round ( ( totalIns - totalOuts ) / tx . virtualSize ( ) ) , feeRate ) ;
2021-03-19 16:29:27 +01:00
assert . strictEqual ( transactionFee . split ( ' ' ) [ 1 ] * 100000000 , totalIns - totalOuts ) ;
2020-09-15 16:02:10 +02:00
2020-09-15 18:11:18 +02:00
if ( device . getPlatform ( ) === 'ios' ) {
console . warn ( 'rest of the test is Android only, skipped' ) ;
return ;
}
2020-09-23 18:42:23 +02:00
// now, testing scanQR with bip21:
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-03-24 18:17:17 +01:00
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to SATS
2020-09-23 18:42:23 +02:00
await element ( by . id ( 'BlueAddressInputScanQrButton' ) ) . tap ( ) ;
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
2020-09-23 18:42:23 +02:00
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
await sleep ( 1000 ) ;
}
const bip21 = 'bitcoin:bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7?amount=0.00015&pj=https://btc.donate.kukks.org/BTC/pj' ;
2020-09-28 15:37:41 +02:00
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( bip21 ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
2020-09-23 18:42:23 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionValue' ) ;
await yo ( 'PayjoinSwitch' ) ;
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 0 ] . script ) , 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
assert . strictEqual ( transaction . outs [ 0 ] . value , 15000 ) ;
2021-03-24 18:17:17 +01:00
// now, testing scanQR with just address after amount set to 1.1 USD. Denomination should not change after qrcode scan
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to SATS
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to FIAT
await element ( by . id ( 'BitcoinAmountInput' ) ) . replaceText ( '1.1' ) ;
await element ( by . id ( 'BlueAddressInputScanQrButton' ) ) . tap ( ) ;
// tapping 5 times invisible button is a backdoor:
for ( let c = 0 ; c <= 5 ; c ++ ) {
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
await sleep ( 1000 ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionValue' ) ;
await yo ( 'PayjoinSwitch' ) ;
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 0 ] . script ) , 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
assert . notEqual ( transaction . outs [ 0 ] . value , 110000000 ) ; // check that it is 1.1 USD, not 1 BTC
assert . ok ( transaction . outs [ 0 ] . value < 10000 ) ; // 1.1 USD ~ 0,00001964 sats in march 2021
2020-09-15 18:11:18 +02:00
// now, testing units switching, and then creating tx with SATS:
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-03-24 18:17:17 +01:00
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to BTC
await element ( by . id ( 'BitcoinAmountInput' ) ) . replaceText ( '0.00015' ) ;
2020-09-15 18:11:18 +02:00
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to sats
2020-09-23 18:42:23 +02:00
assert . strictEqual ( await extractTextFromElementById ( 'BitcoinAmountInput' ) , '15000' ) ;
2020-09-15 18:11:18 +02:00
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to FIAT
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to BTC
2020-09-23 18:42:23 +02:00
assert . strictEqual ( await extractTextFromElementById ( 'BitcoinAmountInput' ) , '0.00015' ) ;
2020-09-15 18:11:18 +02:00
await element ( by . id ( 'changeAmountUnitButton' ) ) . tap ( ) ; // switched to sats
await element ( by . id ( 'BitcoinAmountInput' ) ) . replaceText ( '50000' ) ;
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionValue' ) ;
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( transaction . outs . length , 2 ) ;
assert . strictEqual ( transaction . outs [ 0 ] . value , 50000 ) ;
2021-02-25 15:32:11 +01:00
// now, testing send many feature
await device . pressBack ( ) ;
await device . pressBack ( ) ;
// we already have one output, lest add another two
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddRecipient' ) ) . tap ( ) ;
2021-02-27 14:17:07 +01:00
await yo ( 'Transaction1' ) ; // adding a recipient autoscrolls it to the last one
2021-02-25 15:32:11 +01:00
await element ( by . id ( 'AddressInput' ) . withAncestor ( by . id ( 'Transaction1' ) ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'BitcoinAmountInput' ) . withAncestor ( by . id ( 'Transaction1' ) ) ) . typeText ( '0.0002\n' ) ;
2021-03-12 15:02:49 +01:00
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddRecipient' ) ) . tap ( ) ;
await yo ( 'Transaction2' ) ; // adding a recipient autoscrolls it to the last one
// remove last output, check if second output is shown
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'RemoveRecipient' ) ) . tap ( ) ;
await yo ( 'Transaction1' ) ;
// adding it again
2021-02-25 15:32:11 +01:00
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddRecipient' ) ) . tap ( ) ;
2021-02-27 17:46:14 +01:00
await yo ( 'Transaction2' ) ; // adding a recipient autoscrolls it to the last one
2021-02-25 15:32:11 +01:00
await element ( by . id ( 'AddressInput' ) . withAncestor ( by . id ( 'Transaction2' ) ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'BitcoinAmountInput' ) . withAncestor ( by . id ( 'Transaction2' ) ) ) . typeText ( '0.0003\n' ) ;
// remove second output
await element ( by . id ( 'Transaction2' ) ) . swipe ( 'right' , 'fast' , NaN , 0.2 ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'RemoveRecipient' ) ) . tap ( ) ;
// creating and verifying. tx should have 3 outputs
2021-09-13 19:43:26 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
2021-02-25 15:32:11 +01:00
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( transaction . outs . length , 3 ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 0 ] . script ) , 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
assert . strictEqual ( transaction . outs [ 0 ] . value , 50000 ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 1 ] . script ) , 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
2021-03-12 15:02:49 +01:00
assert . strictEqual ( transaction . outs [ 1 ] . value , 30000 ) ;
2021-02-25 15:32:11 +01:00
2020-09-15 18:11:18 +02:00
// now, testing sendMAX feature:
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-03-12 17:19:04 +01:00
await device . pressBack ( ) ; // go back to wallet tx list to reset the form
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
// set fee rate
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( feeRate + '' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// first send MAX output
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
await element ( by . id ( 'BitcoinAmountInput' ) ) . typeText ( '0.0001\n' ) ;
2020-09-15 18:11:18 +02:00
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'sendMaxButton' ) ) . tap ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-03-12 17:19:04 +01:00
2020-09-15 18:11:18 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
2021-03-12 17:19:04 +01:00
await yo ( 'TransactionDetailsButton' ) ;
2020-09-15 18:11:18 +02:00
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( transaction . outs . length , 1 , 'should be single output, no change' ) ;
assert . ok ( transaction . outs [ 0 ] . value > 100000 ) ;
2021-03-12 17:19:04 +01:00
// add second output with amount
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddRecipient' ) ) . tap ( ) ;
await yo ( 'Transaction1' ) ;
await element ( by . id ( 'AddressInput' ) . withAncestor ( by . id ( 'Transaction1' ) ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'BitcoinAmountInput' ) . withAncestor ( by . id ( 'Transaction1' ) ) ) . typeText ( '0.0001\n' ) ;
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionDetailsButton' ) ;
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . strictEqual ( transaction . outs . length , 2 , 'should be single output, no change' ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 0 ] . script ) , 'bc1qnapskphjnwzw2w3dk4anpxntunc77v6qrua0f7' ) ;
assert . ok ( transaction . outs [ 0 ] . value > 50000 ) ;
assert . strictEqual ( bitcoin . address . fromOutputScript ( transaction . outs [ 1 ] . script ) , 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
assert . strictEqual ( transaction . outs [ 1 ] . value , 10000 ) ;
2021-02-18 14:37:43 +01:00
// now, testing cosign psbt:
2021-09-13 19:43:26 +02:00
await device . pressBack ( ) ;
2021-02-18 14:37:43 +01:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'PsbtSign' ) ) . tap ( ) ;
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2021-02-18 14:37:43 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
await sleep ( 1000 ) ;
}
// 1 input, 2 outputs. wallet can fully sign this tx
const psbt =
'cHNidP8BAFICAAAAAXYa7FEQBAQ2X0B48aHHKKgzkVuHfQ2yCOi3v9RR0IqlAQAAAAAAAACAAegDAAAAAAAAFgAUSnH40G+jiJfreeRb36cs641KFm8AAAAAAAEBH5YVAAAAAAAAFgAUTKHjDm4OJQSbvy9uzyLYi5i5XIoiBgMQcGrP5TIMrdvb73yB4WnZvkPzKr1EzJXJYBHWmlPJZRgAAAAAVAAAgAAAAIAAAACAAQAAAD4AAAAAAA==' ;
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( psbt ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
// this is fully-signed tx, "this is tx hex" help text should appear
await yo ( 'DynamicCode' ) ;
2021-03-02 14:38:02 +01:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
// let's test wallet details screens
await element ( by . id ( 'WalletDetails' ) ) . tap ( ) ;
// rename test
await element ( by . id ( 'WalletNameInput' ) ) . replaceText ( 'testname\n' ) ;
await element ( by . id ( 'Save' ) ) . tap ( ) ;
await sup ( 'OK' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'WalletLabel' ) ) ) . toHaveText ( 'testname' ) ;
await element ( by . id ( 'WalletDetails' ) ) . tap ( ) ;
// wallet export
await element ( by . id ( 'WalletDetailsScroll' ) ) . swipe ( 'up' , 'fast' , 1 ) ;
await element ( by . id ( 'WalletExport' ) ) . tap ( ) ;
await element ( by . id ( 'WalletExportScroll' ) ) . swipe ( 'up' , 'fast' , 1 ) ;
await expect ( element ( by . id ( 'Secret' ) ) ) . toHaveText ( process . env . HD _MNEMONIC _BIP84 ) ;
await device . pressBack ( ) ;
// XPUB
await element ( by . id ( 'WalletDetailsScroll' ) ) . swipe ( 'up' , 'fast' , 1 ) ;
await element ( by . id ( 'XPub' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'BlueCopyTextToClipboard' ) ) ) . toBeVisible ( ) ;
await device . pressBack ( ) ;
// Delete
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await helperDeleteWallet ( 'testname' , '105526' ) ;
2021-02-18 14:37:43 +01:00
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-04-27 10:23:56 +02:00
} ) ;
2020-04-29 16:27:07 +02:00
2020-11-19 15:33:18 +01:00
/ * *
* test plan :
* 1. import wallet
* 2. wallet - > send - > import transaction ( scan QR )
* 3. provide unsigned psbt from coldcard ( UR )
* 4. on psbtWithHardwareWallet , tap scanQr
* 5. provide fully signed psbt ( UR )
* 6. verify that we can see broadcast button and camera backdorr button is NOT visible
* /
it ( 'can import zpub as watch-only, import psbt, and then scan signed psbt' , async ( ) => {
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
}
await helperImportWallet (
'zpub6rDWXE4wbwefeCrHWehXJheXnti5F9PbpamDUeB5eFbqaY89x3jq86JADBuXpnJnSvRVwqkaTnyMaZERUg4BpxD9V4tSZfKeYh1ozPdL1xK' ,
'Imported Watch-only' ,
2021-04-24 23:24:10 +02:00
'0 BTC' , // it used to be 0.00030666 till someone stole it from git history kek
2020-11-19 15:33:18 +01:00
) ;
2021-01-28 06:55:42 +01:00
await element ( by . id ( 'ReceiveButton' ) ) . tap ( ) ;
2021-01-28 09:50:55 +01:00
try {
// in case emulator has no google services and doesnt support pushes
// we just dont show this popup
2021-02-09 14:35:04 +01:00
await element ( by . text ( ` No, and don’ t ask me again ` ) ) . tap ( ) ;
2021-01-28 09:50:55 +01:00
} catch ( _ ) { }
2021-01-28 20:39:19 +01:00
await expect ( element ( by . id ( 'BitcoinAddressQRCodeContainer' ) ) ) . toBeVisible ( ) ;
2021-01-28 20:34:45 +01:00
await expect ( element ( by . text ( 'bc1qtc9zquvq7lgq87kzsgltvv4etwm9uxphfkvkay' ) ) ) . toBeVisible ( ) ;
2021-01-28 06:55:42 +01:00
await element ( by . id ( 'SetCustomAmountButton' ) ) . tap ( ) ;
2021-07-06 11:38:53 +02:00
await element ( by . id ( 'BitcoinAmountInput' ) ) . replaceText ( '1' ) ;
2021-01-28 20:34:45 +01:00
await element ( by . id ( 'CustomAmountDescription' ) ) . typeText ( 'Test' ) ;
2021-01-28 06:55:42 +01:00
await element ( by . id ( 'CustomAmountSaveButton' ) ) . tap ( ) ;
2021-07-06 11:38:53 +02:00
await sup ( '1 BTC' ) ;
await sup ( 'Test' ) ;
2021-01-28 20:39:19 +01:00
await expect ( element ( by . id ( 'BitcoinAddressQRCodeContainer' ) ) ) . toBeVisible ( ) ;
2021-01-28 20:34:45 +01:00
await expect ( element ( by . text ( 'bitcoin:bc1qtc9zquvq7lgq87kzsgltvv4etwm9uxphfkvkay?amount=1&label=Test' ) ) ) . toBeVisible ( ) ;
2021-01-28 06:55:42 +01:00
await device . pressBack ( ) ;
2020-11-19 15:33:18 +01:00
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'ImportQrTransactionButton' ) ) . tap ( ) ; // opens camera
const unsignedPsbt =
'ur:bytes/tzahqumzwnlszqzjqgqqqqqp6uu247pvcz6zld9p77ghlnl753q8fgygggzv9ugjxsmggyy5gqcqqqqqqqq0llllluqepssqqqqqqqqqzcqpfkxmzh6ud2yrvcl37uyy9yswr2z4mx276qqqqqqqqqgpragvxqqqqqqqqqqkqq2tgxjzwa0000egemyzygsv92j2zdwvg5ejypszwe3qctjvrwul6t2ts7yhk8e5takxwzey2z70kdnykwd43jsptrzps95d6cp4gqqqsqqqqqyqqqqqpqqqqqqqqpqqqqqqqqq0vr0lj' ;
const signedPsbt =
'ur:bytes/tyqjuurnvf607qgq2gpqqqqqq8tn32hc9nqtgta558mezl70l6jyqa9q3ppqfsh3zg6rdpqsj3qrqqqqqqqqpllllllsryxzqqqqqqqqqqtqq9xcmv2lt34gsdnr78msss5jpcdg2hvetmgqqqqqqqqpqy04pscqqqqqqqqqzcqpfdq6gfm4aaal9r8vsg3zps42fgf4e3znxgszqfmxyrpwfsdmnlfdfwrcj7clx30kcecty3gte7ekvjeekkx2q9vvgjpsg5pzzqxjc9xv3rlhu2n6u87pm94agwcmvcywwsx9k0jpvwyng8crytgrkcpzqae6amp5xy03x2lsklv5zgnmeht0grzns27tmsjtsg2j0ne2969kqyqsxpqpqqqqqgsxqfmxyrpwfsdmnlfdfwrcj7clx30kcecty3gte7ekvjeekkx2q9vvgxqk3htqx4qqqzqqqqqqsqqqqqyqqqqqqqqyqqqqqqqqear8ke' ;
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
await sleep ( 1000 ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( unsignedPsbt ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
// now lets test scanning back QR with UR PSBT. this should lead straight to broadcast dialog
await element ( by . id ( 'PsbtWithHardwareScrollView' ) ) . swipe ( 'up' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
await element ( by . id ( 'PsbtTxScanButton' ) ) . tap ( ) ; // opening camera
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
await sleep ( 1000 ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( signedPsbt ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'ScanQrBackdoorButton' ) ) ) . toBeNotVisible ( ) ;
await yo ( 'PsbtWithHardwareWalletBroadcastTransactionButton' ) ;
2020-09-14 19:02:24 +02:00
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await helperDeleteWallet ( 'Imported Watch-only' ) ;
2020-05-23 08:50:08 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-04-29 16:27:07 +02:00
} ) ;
2020-08-11 20:16:24 +02:00
it ( 'should handle URL successfully' , async ( ) => {
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
2020-10-06 13:23:13 +02:00
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
2020-08-11 20:16:24 +02:00
}
if ( ! process . env . HD _MNEMONIC _BIP84 ) {
console . error ( 'process.env.HD_MNEMONIC_BIP84 not set, skipped' ) ;
return ;
}
await helperImportWallet ( process . env . HD _MNEMONIC _BIP84 , 'Imported HD SegWit (BIP84 Bech32 Native)' , '0.00105526 BTC' ) ;
await device . launchApp ( {
newInstance : true ,
url : 'bitcoin:BC1QH6TF004TY7Z7UN2V5NTU4MKF630545GVHS45U7\\?amount=0.0001\\&label=Yo' ,
} ) ;
2020-12-18 15:23:48 +01:00
// setting fee rate:
const feeRate = 2 ;
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( feeRate + '' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-13 19:43:26 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
2020-08-11 20:16:24 +02:00
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
// created. verifying:
await yo ( 'TransactionValue' ) ;
expect ( element ( by . id ( 'TransactionValue' ) ) ) . toHaveText ( '0.0001' ) ;
expect ( element ( by . id ( 'TransactionAddress' ) ) ) . toHaveText ( 'BC1QH6TF004TY7Z7UN2V5NTU4MKF630545GVHS45U7' ) ;
2020-11-05 18:17:27 +01:00
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await helperDeleteWallet ( 'Imported HD SegWit (BIP84 Bech32 Native)' , '105526' ) ;
2020-11-05 18:17:27 +01:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-08-11 20:16:24 +02:00
} ) ;
2020-11-04 20:41:40 +01:00
2020-11-05 18:17:27 +01:00
it ( 'can import multisig setup from UR (ver1) QRs (2 frames), and create tx' , async ( ) => {
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
}
2020-11-04 20:41:40 +01:00
await yo ( 'WalletsList' ) ;
await element ( by . id ( 'WalletsList' ) ) . swipe ( 'left' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
// going to Import Wallet screen and importing mnemonic
await element ( by . id ( 'CreateAWallet' ) ) . tap ( ) ;
await element ( by . id ( 'ImportWallet' ) ) . tap ( ) ;
await element ( by . id ( 'ScanImport' ) ) . tap ( ) ;
const urs = [
'UR:BYTES/1OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/TYQMJGEQGFK82E2HV9KXCET5YPXH2MR5D9EKJEEQWDJHGATSYPNXJMR9PG3JQARGD9EJQENFD3JJQCM0DE6XZ6TWWVSX7MNV0YS8QATZD35KXGRTV4UHXGRPDEJZQ6TNYPEKZEN9YP6X7Z3RYPJXJUM5WF5KYAT5V5SXZMT0DENJQCM0WD5KWMN9WFES5GC2FESK6EF6YPXH2MR5D9EKJEEQ2ESH2MR5PFGX7MRFVDUN5GPJYPHKVGPJPFZX2UNFWESHG6T0DCAZQMF0XSUZWTESYUHNQFE0XGNS53N0WFKKZAP6YPGRY46NFQ9Q53PNXAZ5Z3PC8QAZQKNSW43RWDRFDFCXV6Z92F9YU6NGGD94S5NNWP2XGNZ22C6K2M69D4F4YKNYFPC5GANS' ,
'UR:BYTES/2OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/8944VARY2EZHJ62CDVMHQKRC2F3XVKN629M8X3ZXWPNYGJZ9FPT8G4NS0Q6YG73EG3R42468DCE9S6E40FRN2AF5X4G4GNTNT9FNYAN2DA5YU5G2PGCNVWZYGSMRQVE6YPD8QATZXU6K6S298PZK57TC2DAX772SD4RKUEP4G5MY672YXAQ5C36WDEJ8YA2HWC6NY7RS0F5K6KJ3FD6KKAMKG4N9S4ZGW9K5SWRWVF3XXDNRVDGR2APJV9XNXMTHWVEHQJ6E2DHYKUZTF4XHJARYVF8Y2KJX24UYK7N6W3V5VNFC2PHQ5ZSJDYL5T' ,
] ;
await waitFor ( element ( by . id ( 'UrProgressBar' ) ) ) . toBeNotVisible ( ) ;
for ( const ur of urs ) {
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
2020-11-04 20:41:40 +01:00
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( ur ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'UrProgressBar' ) ) ) . toBeVisible ( ) ;
}
if ( process . env . TRAVIS ) 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
const expectedWalletLabel = 'Multisig Vault' ;
await element ( by . text ( expectedWalletLabel ) ) . tap ( ) ;
2020-11-05 18:17:27 +01:00
// sending...
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'BitcoinAmountInput' ) ) . typeText ( '0.0005\n' ) ;
// setting fee rate:
const feeRate = 1 ;
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( feeRate + '' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
try {
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
} catch ( _ ) { }
await waitFor ( element ( by . id ( 'ItemUnsigned' ) ) ) . toBeVisible ( ) ;
await waitFor ( element ( by . id ( 'ItemSigned' ) ) ) . toBeNotVisible ( ) ; // not a single green checkmark
await element ( by . id ( 'ProvideSignature' ) ) . tap ( ) ;
await element ( by . id ( 'CosignedScanOrImportFile' ) ) . tap ( ) ;
const ursSignedByColdcard = [
'UR:BYTES/1OF3/AZ7UYD5YRTJGJHEPJC045UPYV6SRFURJALR0968WCXHP0ZZ9HK0SV8YW98/TYP6XURNVF607QGQ05PQQQQQQ89FQE9NVDRHTESWDDWCDCAMW53Z2H5U50T6ZRT2JVDQF3Y4QCJESQQQQQQQPLLLLLLSY5XRQQQQQQQQQQTQQ9R75WZLX547D94TPAHFFG8WPC7X6JC555C7U5QQQQQQQQQZYQPQTAH0P50D4QLFTN049K7LLDCWH7CS3ZKJY9G8XEGV63P308HSH9ZSQQQQQQQQZQ82QGQQQQQQQYQUWDNXP9500ELRFUSTVYLPAQJC34GUCHG52MH2665NTY5WSCJS72GPQQQQQQQPQQQGQQJS4YQSQQQQQQQZYQPQY8RVFMEZUPXNJSKTRUVR389TNY9XLLX7JNYE08K5FJP5R8A2UFN6TYSZQQQQQQQQZCQPFRSXCZANQ56CMLH79RF2KUUEZTAXF7QE6QJ8XPZQYGPSL9CGL7X73GC8MACPM7SNEHE2FCAP5LRUG436ZNX6YF39R7E5Y5PZQ4CAXM6KQM64TAC99DCV6THY7U9S4VL8N5XR53FX55WLPQSG4H86QYSSYUSJAUY6JC734K9PDVN9C72HFZ84ELQ9E4W8GP66RAQ2HQY3YRH8QQQQQQQPQY44P2GPQQQQQQQQYGQZQGWXCNHJ9CZD89PVK8CC8ZW2HXG2DL7DA9XFJ70DGNYRGX' ,
'UR:BYTES/2OF3/AZ7UYD5YRTJGJHEPJC045UPYV6SRFURJALR0968WCXHP0ZZ9HK0SV8YW98/064CN8YGPQXR4WY5H9Q3AE3PTAKH7XX0ZET9N09T8UFWJX9NXA59L7APH8X2ERGUCYGQ3QFQ4CKRKRQP9ZC7T85KSRA46NT9FYEKCLPZQ9X7RNA3QL9MS4Y0XSYGQSNZX89397SCJFJ4AP9UAS7PX5R0RMDTQN6YLGKPT53D05LN7QGCQSZP282GSSXR4WY5H9Q3AE3PTAKH7XX0ZET9N09T8UFWJX9NXA59L7APH8X2ERYYPN4DHVYFGS5VWQ69YPL8DE0DRZPSNPH84FTN4QK7UWHNPZP4SDQPZJ4C3QVQCW4CJJU5Z8HXY90K6LCCEUT9VKDU4VL396GCKVMKSHLM5XUUETYVWPDRWKQVCQQQYQQQQQPQQQQQQGQQSQQZQQQQQQQQPSQQQQYGRQXW4KAS39ZZ33CRG5S8UAH9A5VGXZVXU749WW5ZMM367VYGXKP5QYRNFHATVGXQQQPQQQQQQGQQQQQZQQYQQQSQQQQQQQQVQQQQQQQQQSZ36JYYP33R60ZM9YYRTYN73MJKP6UZQANNWM7GRRMX067ATJGTQG0EZ0CDFPQDP6C5KEPNKKXMNPYYPVRWLRWVQ76ZKKPKAH9K84NZRE00DMXZHGC54WYGPQXXY0FUTV5SSDVJ068W2C8TSGRKWDM0EQV0VELTM4' ,
'UR:BYTES/3OF3/AZ7UYD5YRTJGJHEPJC045UPYV6SRFURJALR0968WCXHP0ZZ9HK0SV8YW98/WFPVPPLYFLP4RNFHATVGXQQQPQQQQQQGQQQQQZQQYQQQSQQSQQQQQQQQQQPZQGP58TZJMYXW6CMWVYSS9SDMUDESRMG26CXMKUKC7KVG09AAHVC2ARQUZ6XAVQESQQQGQQQQQZQQQQQQSQPQQQYQQYQQQQQQQQQQQQQH8NSKY' ,
] ;
for ( const ur of ursSignedByColdcard ) {
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
2020-11-05 18:17:27 +01:00
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( ur ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'UrProgressBar' ) ) ) . toBeVisible ( ) ;
}
await waitFor ( element ( by . id ( 'ItemSigned' ) ) ) . toBeVisible ( ) ; // one green checkmark visible
await element ( by . id ( 'ProvideSignature' ) ) . tap ( ) ;
await element ( by . id ( 'CosignedScanOrImportFile' ) ) . tap ( ) ;
const urSignedByColdcardAndCobo = [
'UR:BYTES/1OF3/CL7WUCY4FVHCWAA0TRRKJ0A5CA3JZTYP2L9PS2ZMPUG59ARUW2US09Z73L/TYP5CURNVF607QGQ05PQQQQQQ89FQE9NVDRHTESWDDWCDCAMW53Z2H5U50T6ZRT2JVDQF3Y4QCJESQQQQQQQPLLLLLLSY5XRQQQQQQQQQQTQQ9R75WZLX547D94TPAHFFG8WPC7X6JC555C7U5QQQQQQQQQZYQPQTAH0P50D4QLFTN049K7LLDCWH7CS3ZKJY9G8XEGV63P308HSH9ZSQQQQQQQQZQ82QGQQQQQQQYQUWDNXP9500ELRFUSTVYLPAQJC34GUCHG52MH2665NTY5WSCJS72GPQQQQQQQPQQQGQQJS4YQSQQQQQQQZYQPQY8RVFMEZUPXNJSKTRUVR389TNY9XLLX7JNYE08K5FJP5R8A2UFN6TYSZQQQQQQQQZCQPFRSXCZANQ56CMLH79RF2KUUEZTAXF7QE6QJ8XPZQYGPSL9CGL7X73GC8MACPM7SNEHE2FCAP5LRUG436ZNX6YF39R7E5Y5PZQ4CAXM6KQM64TAC99DCV6THY7U9S4VL8N5XR53FX55WLPQSG4H86QYSSYUSJAUY6JC734K9PDVN9C72HFZ84ELQ9E4W8GP66RAQ2HQY3YRH8QQQQQQQPQY44P2GPQQQQQQQQYGQZQGWXCNHJ9CZD89PVK8CC8ZW2HXG2DL7DA9XFJ70DGNYRGX' ,
'UR:BYTES/2OF3/CL7WUCY4FVHCWAA0TRRKJ0A5CA3JZTYP2L9PS2ZMPUG59ARUW2US09Z73L/064CN8QYYDKPQQGUCYGQ3QFQ4CKRKRQP9ZC7T85KSRA46NT9FYEKCLPZQ9X7RNA3QL9MS4Y0XSYGQSNZX89397SCJFJ4AP9UAS7PX5R0RMDTQN6YLGKPT53D05LN7QGCQ5SVZ9QGSSP5QHQ4KYKXRNW60UDHHKQVXK0M9XNETCY9P5EA2JUFVU0YTYNJZAQGSZQAYTRQWNMLYTLYL9UN4V4TZ6LPU4YYJNKRJLSZDNPAQRXR90LEQPGAFZZQCW4CJJU5Z8HXY90K6LCCEUT9VKDU4VL396GCKVMKSHLM5XUUETYVSSXW4KAS39ZZ33CRG5S8UAH9A5VGXZVXU749WW5ZMM367VYGXKP5QY22HQQQQPQ9R4YGGRRZ8579K2GGXKF8ARH9VR4CYPM8XAHUSX8KVL4A6HYSKQSLJYLS6JZQ6R43FDJR8DVDHXZGGZCXA7XUCPA59DVRDMWTV0TXY8J77MKV9W33F2UGSZQVVG7NCKEFPQ6EYL5WU4SWHQS8VUMKLJQC7EN7HH2UJZCZR7GN7R28XN06KCSVQQQZQQQQQQSQQQQQYQQGQQPQQPQQQQQQQQQQQZYQSRGWK99KGVA43KUCFPQTQMHCMNQ8KS44SDHDED3AVCS7TMMWES46XPC95D6CPNQQQQSQQQQQYQQQQQ' ,
'UR:BYTES/3OF3/CL7WUCY4FVHCWAA0TRRKJ0A5CA3JZTYP2L9PS2ZMPUG59ARUW2US09Z73L/PQQZQQQGQQGQQQQQQQQQQQQQGND7FE' ,
] ;
for ( const ur of urSignedByColdcardAndCobo ) {
2021-03-24 18:17:17 +01:00
// tapping 5 times invisible button is a backdoor:
2020-11-19 15:33:18 +01:00
for ( let c = 0 ; c <= 5 ; c ++ ) {
2020-11-05 18:17:27 +01:00
await element ( by . id ( 'ScanQrBackdoorButton' ) ) . tap ( ) ;
}
await element ( by . id ( 'scanQrBackdoorInput' ) ) . replaceText ( ur ) ;
await element ( by . id ( 'scanQrBackdoorOkButton' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'UrProgressBar' ) ) ) . toBeVisible ( ) ;
}
await waitFor ( element ( by . id ( 'ExportSignedPsbt' ) ) ) . toBeVisible ( ) ;
await element ( by . id ( 'PsbtMultisigConfirmButton' ) ) . tap ( ) ;
// created. verifying:
await yo ( 'TransactionValue' ) ;
expect ( element ( by . id ( 'TransactionValue' ) ) ) . toHaveText ( '0.0005' ) ;
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
const txhex = await extractTextFromElementById ( 'TxhexInput' ) ;
const transaction = bitcoin . Transaction . fromHex ( txhex ) ;
assert . ok ( transaction . ins . length === 1 || transaction . ins . length === 2 ) ; // depending on current fees gona use either 1 or 2 inputs
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 ) ;
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await helperDeleteWallet ( expectedWalletLabel , '108880' ) ;
2020-11-05 18:17:27 +01:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-08-11 20:16:24 +02:00
} ) ;
2020-11-04 17:24:39 +01:00
it ( 'can manage UTXO' , async ( ) => {
2020-11-06 08:15:59 +01:00
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
}
2021-09-17 14:26:09 +02:00
await helperImportWallet ( process . env . HD _MNEMONIC _BIP84 , 'Imported HD SegWit (BIP84 Bech32 Native)' , '0.00105526 BTC' ) ;
2020-11-04 17:24:39 +01:00
// refresh transactions
await element ( by . id ( 'refreshTransactions' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'NoTxBuyBitcoin' ) ) )
. not . toExist ( )
. withTimeout ( 300 * 1000 ) ;
// change note of 0.001 tx output
await element ( by . text ( '0.001' ) ) . atIndex ( 0 ) . tap ( ) ;
2021-09-06 17:10:09 +02:00
await element ( by . text ( 'Details' ) ) . tap ( ) ;
2020-11-04 17:24:39 +01:00
await expect ( element ( by . text ( '49944e90fe917952e36b1967cdbc1139e60c89b4800b91258bf2345a77a8b888' ) ) ) . toBeVisible ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( 'test1' ) ;
await element ( by . text ( 'Save' ) ) . tap ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// back to wallet screen
await device . pressBack ( ) ;
await device . pressBack ( ) ;
// open CoinControl
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'CoinControl' ) ) . tap ( ) ;
await waitFor ( element ( by . id ( 'Loading' ) ) ) // wait for outputs to be loaded
. not . toExist ( )
. withTimeout ( 300 * 1000 ) ;
await expect ( element ( by . text ( 'test1' ) ) . atIndex ( 0 ) ) . toBeVisible ( ) ;
// change output note and freeze it
await element ( by . text ( 'test1' ) ) . atIndex ( 0 ) . tap ( ) ;
await element ( by . id ( 'OutputMemo' ) ) . replaceText ( 'test2' ) ;
await element ( by . type ( 'android.widget.CompoundButton' ) ) . tap ( ) ; // freeze switch
await device . pressBack ( ) ; // closing modal
await expect ( element ( by . text ( 'test2' ) ) . atIndex ( 0 ) ) . toBeVisible ( ) ;
2020-12-10 17:44:14 +01:00
await expect ( element ( by . text ( 'Freeze' ) ) . atIndex ( 0 ) ) . toBeVisible ( ) ;
2020-11-04 17:24:39 +01:00
// use frozen output to create tx using "Use coin" feature
await element ( by . text ( 'test2' ) ) . atIndex ( 0 ) . tap ( ) ;
await element ( by . id ( 'UseCoin' ) ) . tap ( ) ;
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'sendMaxButton' ) ) . tap ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// setting fee rate:
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( '1' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-13 19:43:26 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
2020-11-04 17:24:39 +01:00
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
2021-09-17 14:26:09 +02:00
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
2020-11-04 17:24:39 +01:00
2021-09-17 14:26:09 +02:00
const txhex1 = await extractTextFromElementById ( 'TxhexInput' ) ;
const tx1 = bitcoin . Transaction . fromHex ( txhex1 ) ;
assert . strictEqual ( tx1 . outs . length , 1 ) ;
assert . strictEqual ( tx1 . outs [ 0 ] . script . toString ( 'hex' ) , '00147ea385f352be696ab0f6e94a0ee0e3c6d4b14a53' ) ;
assert . strictEqual ( tx1 . outs [ 0 ] . value , 99888 ) ;
assert . strictEqual ( tx1 . ins . length , 1 ) ;
assert . strictEqual ( tx1 . ins [ 0 ] . hash . toString ( 'hex' ) , '88b8a8775a34f28b25910b80b4890ce63911bccd67196be3527991fe904e9449' ) ;
assert . strictEqual ( tx1 . ins [ 0 ] . index , 0 ) ;
2020-11-04 17:24:39 +01:00
// back to wallet screen
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-09-17 14:26:09 +02:00
await device . pressBack ( ) ;
2020-11-04 17:24:39 +01:00
// create tx with unfrozen input
await element ( by . id ( 'SendButton' ) ) . tap ( ) ;
await element ( by . id ( 'AddressInput' ) ) . replaceText ( 'bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl' ) ;
await element ( by . id ( 'advancedOptionsMenuButton' ) ) . tap ( ) ;
await element ( by . id ( 'sendMaxButton' ) ) . tap ( ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
// setting fee rate:
await element ( by . id ( 'chooseFee' ) ) . tap ( ) ;
await element ( by . id ( 'feeCustom' ) ) . tap ( ) ;
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( '1' ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
2021-09-13 19:43:26 +02:00
if ( process . env . TRAVIS ) await sleep ( 5000 ) ;
2020-11-04 17:24:39 +01:00
await element ( by . id ( 'CreateTransactionButton' ) ) . tap ( ) ;
2021-09-17 14:26:09 +02:00
await element ( by . id ( 'TransactionDetailsButton' ) ) . tap ( ) ;
const txhex2 = await extractTextFromElementById ( 'TxhexInput' ) ;
const tx2 = bitcoin . Transaction . fromHex ( txhex2 ) ;
2020-11-04 17:24:39 +01:00
2021-09-17 14:26:09 +02:00
assert . strictEqual ( tx2 . outs . length , 1 ) ;
assert . strictEqual ( tx2 . outs [ 0 ] . script . toString ( 'hex' ) , '00147ea385f352be696ab0f6e94a0ee0e3c6d4b14a53' ) ;
assert . strictEqual ( tx2 . outs [ 0 ] . value , 5414 ) ;
assert . strictEqual ( tx2 . ins . length , 1 ) ;
assert . strictEqual ( tx2 . ins [ 0 ] . hash . toString ( 'hex' ) , '761aec51100404365f4078f1a1c728a833915b877d0db208e8b7bfd451d08aa5' ) ;
assert . strictEqual ( tx2 . ins [ 0 ] . index , 1 ) ;
2020-11-06 08:15:59 +01:00
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await device . pressBack ( ) ;
2021-09-17 14:26:09 +02:00
await device . pressBack ( ) ;
await helperDeleteWallet ( 'Imported HD SegWit (BIP84 Bech32 Native)' , '105526' ) ;
2021-09-16 20:56:09 +02:00
2020-11-06 08:15:59 +01:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
2020-11-04 17:24:39 +01:00
} ) ;
2021-07-20 16:36:44 +02:00
it ( 'can import HD wallet with a passphrase' , async ( ) => {
const lockFile = '/tmp/travislock.' + hashIt ( jasmine . currentTest . fullName ) ;
if ( process . env . TRAVIS ) {
if ( require ( 'fs' ) . existsSync ( lockFile ) )
return console . warn ( 'skipping' , JSON . stringify ( jasmine . currentTest . fullName ) , 'as it previously passed on Travis' ) ;
}
await helperImportWallet (
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' ,
'Imported HD SegWit (BIP84 Bech32 Native)' ,
'0 BTC' ,
'BlueWallet' ,
) ;
await element ( by . id ( 'ReceiveButton' ) ) . tap ( ) ;
try {
// in case emulator has no google services and doesnt support pushes
// we just dont show this popup
await element ( by . text ( ` No, and don’ t ask me again ` ) ) . tap ( ) ;
} catch ( _ ) { }
await yo ( 'BitcoinAddressQRCodeContainer' ) ;
// check if imported wallet has correct recive address
await expect ( element ( by . id ( 'AddressValue' ) ) ) . toHaveText ( 'bc1qe8q660wfj6uvqg7zyn86jcsux36natklqnfdrc' ) ;
2021-09-16 20:56:09 +02:00
await device . pressBack ( ) ;
await device . pressBack ( ) ;
await helperDeleteWallet ( 'Imported HD SegWit (BIP84 Bech32 Native)' ) ;
2021-07-20 16:36:44 +02:00
process . env . TRAVIS && require ( 'fs' ) . writeFileSync ( lockFile , '1' ) ;
} ) ;
2020-03-16 14:51:08 +01:00
} ) ;
2020-03-19 16:51:35 +01:00
async function sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
}
2020-03-19 17:55:35 +01:00
async function yo ( id , timeout = 33000 ) {
2020-03-19 16:51:35 +01:00
return waitFor ( element ( by . id ( id ) ) )
. toBeVisible ( )
. withTimeout ( timeout ) ;
}
2020-04-27 10:23:56 +02:00
async function sup ( text , timeout = 33000 ) {
return waitFor ( element ( by . text ( text ) ) )
. toBeVisible ( )
. withTimeout ( timeout ) ;
}
2020-03-19 16:51:35 +01:00
async function helperCreateWallet ( walletName ) {
2020-10-24 19:20:59 +02:00
await element ( by . id ( 'WalletsList' ) ) . swipe ( 'left' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
2020-03-19 16:51:35 +01:00
await element ( by . id ( 'CreateAWallet' ) ) . tap ( ) ;
2020-09-14 19:02:24 +02:00
await element ( by . id ( 'WalletNameInput' ) ) . replaceText ( walletName || 'cr34t3d' ) ;
2020-03-19 17:55:35 +01:00
await yo ( 'ActivateBitcoinButton' ) ;
2020-03-19 16:51:35 +01:00
await element ( by . id ( 'ActivateBitcoinButton' ) ) . tap ( ) ;
await element ( by . id ( 'ActivateBitcoinButton' ) ) . tap ( ) ;
// why tf we need 2 taps for it to work..? mystery
await element ( by . id ( 'Create' ) ) . tap ( ) ;
2020-03-19 17:55:35 +01:00
await yo ( 'PleaseBackupScrollView' ) ;
2020-03-19 16:51:35 +01:00
await element ( by . id ( 'PleaseBackupScrollView' ) ) . swipe ( 'up' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
2020-03-19 17:55:35 +01:00
await yo ( 'PleasebackupOk' ) ;
2020-03-19 16:51:35 +01:00
await element ( by . id ( 'PleasebackupOk' ) ) . tap ( ) ;
await expect ( element ( by . id ( 'WalletsList' ) ) ) . toBeVisible ( ) ;
2020-10-24 19:20:59 +02:00
await element ( by . id ( 'WalletsList' ) ) . swipe ( 'right' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
2020-03-19 16:51:35 +01:00
await expect ( element ( by . id ( walletName || 'cr34t3d' ) ) ) . toBeVisible ( ) ;
}
2020-04-29 16:27:07 +02:00
2021-07-20 16:36:44 +02:00
async function helperImportWallet ( importText , expectedWalletLabel , expectedBalance , passphrase ) {
2020-04-29 16:27:07 +02:00
await yo ( 'WalletsList' ) ;
2021-02-26 11:27:42 +01:00
2020-10-24 19:20:59 +02:00
await element ( by . id ( 'WalletsList' ) ) . swipe ( 'left' , 'fast' , 1 ) ; // in case emu screen is small and it doesnt fit
2020-04-29 16:27:07 +02:00
// going to Import Wallet screen and importing mnemonic
await element ( by . id ( 'CreateAWallet' ) ) . tap ( ) ;
await element ( by . id ( 'ImportWallet' ) ) . tap ( ) ;
2020-09-14 19:02:24 +02:00
await element ( by . id ( 'MnemonicInput' ) ) . replaceText ( importText ) ;
2021-02-26 11:27:42 +01:00
let retries = 0 ;
while ( true ) {
retries = retries + 1 ;
try {
await element ( by . id ( 'DoImport' ) ) . tap ( ) ;
} catch ( _ ) { }
2021-07-20 16:36:44 +02:00
let passphraseAsked = false ;
try {
await sup ( 'Passphrase' , 3000 ) ;
passphraseAsked = true ;
} catch ( e ) { } // passphrase not asked
if ( passphraseAsked ) {
// enter passphrase if needed
if ( passphrase ) {
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( passphrase ) ;
}
await element ( by . text ( 'OK' ) ) . tap ( ) ;
}
2021-02-26 11:27:42 +01:00
if ( process . env . TRAVIS ) await sleep ( 60000 ) ;
// waiting for import result
await sup ( 'OK' , 3 * 61000 ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
try {
await expect ( element ( by . id ( 'ImportError' ) ) ) . not . toBeVisible ( ) ;
break ; // import succeded
} catch ( e ) {
// exit after two failed attempts
if ( retries === 2 ) break ;
// restart import
await element ( by . id ( 'ImportError' ) ) . tap ( ) ;
await element ( by . text ( 'Try again' ) ) . tap ( ) ;
}
}
2020-04-29 16:27:07 +02:00
// lets go inside wallet
await element ( by . text ( expectedWalletLabel ) ) . tap ( ) ;
// label might change in the future
expect ( element ( by . id ( 'WalletBalance' ) ) ) . toHaveText ( expectedBalance ) ;
}
2020-05-23 08:50:08 +02:00
function hashIt ( s ) {
2020-06-01 14:54:23 +02:00
return createHash ( 'sha256' ) . update ( s ) . digest ( ) . toString ( 'hex' ) ;
2020-05-23 08:50:08 +02:00
}
2020-09-15 18:11:18 +02:00
/ * *
* a hack to extract element text . warning , this might break in future
* @ see https : //github.com/wix/detox/issues/445
*
* @ returns { Promise < string > }
* /
async function extractTextFromElementById ( id ) {
try {
await expect ( element ( by . id ( id ) ) ) . 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 ) ;
return 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 ( ',' ) ;
2021-05-08 08:41:45 +02:00
const combineText = value . find ( i => i . includes ( 'text=' ) ) . trim ( ) ;
2020-09-15 18:11:18 +02:00
const [ , elementText ] = combineText . split ( '=' ) ;
return elementText ;
}
}
}
2021-03-04 08:06:07 +01:00
const expectToBeVisible = async id => {
try {
await expect ( element ( by . id ( id ) ) ) . toBeVisible ( ) ;
return true ;
} catch ( e ) {
return false ;
}
} ;
2021-09-16 20:56:09 +02:00
async function helperDeleteWallet ( label , remainingBalanceSat = false ) {
await element ( by . text ( label ) ) . tap ( ) ;
await element ( by . id ( 'WalletDetails' ) ) . tap ( ) ;
await element ( by . id ( 'WalletDetailsScroll' ) ) . swipe ( 'up' , 'fast' , 1 ) ;
await element ( by . id ( 'DeleteButton' ) ) . tap ( ) ;
await sup ( 'Yes, delete' ) ;
await element ( by . text ( 'Yes, delete' ) ) . tap ( ) ;
if ( remainingBalanceSat ) {
await element ( by . type ( 'android.widget.EditText' ) ) . typeText ( remainingBalanceSat ) ;
await element ( by . text ( 'OK' ) ) . tap ( ) ;
}
await expect ( element ( by . id ( 'NoTransactionsMessage' ) ) ) . toBeVisible ( ) ;
}