Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion test/helpers/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,11 @@ export type ToastId =
| 'ReceivedTransactionReplacedToast'
| 'TransactionReplacedToast'
| 'TransactionUnconfirmedToast'
| 'TransactionRemovedToast';
| 'TransactionRemovedToast'
| 'InvalidAddressToast'
| 'ExpiredLightningToast'
| 'InsufficientSpendingToast'
| 'InsufficientSavingsToast';

export async function waitForToast(
toastId: ToastId,
Expand Down
4 changes: 1 addition & 3 deletions test/helpers/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ export function getRnAppPath(): string {
const fallback = path.join(__dirname, '..', '..', 'aut', 'bitkit_rn_regtest.apk');
const appPath = process.env.RN_APK_PATH ?? fallback;
if (!fs.existsSync(appPath)) {
throw new Error(
`RN APK not found at: ${appPath}. Set RN_APK_PATH or place it at ${fallback}`
);
throw new Error(`RN APK not found at: ${appPath}. Set RN_APK_PATH or place it at ${fallback}`);
}
return appPath;
}
Expand Down
11 changes: 9 additions & 2 deletions test/specs/migration.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { elementById, restoreWallet, sleep, tap, typeText, waitForSetupWalletScreenFinish } from '../helpers/actions';
import {
elementById,
restoreWallet,
sleep,
tap,
typeText,
waitForSetupWalletScreenFinish,
} from '../helpers/actions';
import { ciIt } from '../helpers/suite';
import { getNativeAppPath, getRnAppPath, reinstallAppFromPath } from '../helpers/setup';

Expand Down Expand Up @@ -41,7 +48,7 @@ async function restoreLegacyRnWallet(seed: string) {
await waitForSetupWalletScreenFinish();

const getStarted = await elementById('GetStartedButton');
await getStarted.waitForDisplayed( { timeout: 120000 });
await getStarted.waitForDisplayed({ timeout: 120000 });
await tap('GetStartedButton');
await sleep(1000);
}
36 changes: 36 additions & 0 deletions test/specs/numberpad.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BitcoinJsonRpc from 'bitcoin-json-rpc';
import {
completeOnboarding,
enterAddress,
Expand All @@ -8,14 +9,30 @@ import {
tap,
doNavigationClose,
expectTextWithin,
receiveOnchainFunds,
elementById,
} from '../helpers/actions';
import initElectrum from '../helpers/electrum';
import { launchFreshApp, reinstallApp } from '../helpers/setup';
import { ciIt } from '../helpers/suite';
import { bitcoinURL } from '../helpers/constants';

describe('@numberpad - NumberPad', () => {
let electrum: Awaited<ReturnType<typeof initElectrum>> | undefined;
const rpc = new BitcoinJsonRpc(bitcoinURL);
before(async () => {
let balance = await rpc.getBalance();
const address = await rpc.getNewAddress();

while (balance < 10) {
await rpc.generateToAddress(10, address);
balance = await rpc.getBalance();
}

electrum = await initElectrum();
await reinstallApp();
await completeOnboarding();
await receiveOnchainFunds(rpc, { sats: 10_000 });
});

beforeEach(async () => {
Expand Down Expand Up @@ -74,9 +91,12 @@ async function modernDenominationChecks(mode: NumberpadMode) {

await tap('N000');
await expectText('123 000');
await checkContinueButton(mode, { aboveBalance: true });

// Switch to USD
await tap(`${mode}NumberPadUnit`);
await checkContinueButton(mode, { aboveBalance: true });

// reset to 0
await multiTap('NRemove', 8);
if (mode === 'Send') {
Expand All @@ -94,6 +114,7 @@ async function modernDenominationChecks(mode: NumberpadMode) {
await tap('NDecimal');
await tap('N1');
await expectText('1.01');
await checkContinueButton(mode, { aboveBalance: false });

// Switch back to BTC
await tap(`${mode}NumberPadUnit`);
Expand Down Expand Up @@ -134,6 +155,21 @@ async function classicDenominationChecks(mode: NumberpadMode) {

// still there
await expectText('4.20690000');
await checkContinueButton(mode);
}

async function checkContinueButton(
mode: NumberpadMode,
{ aboveBalance = true }: { aboveBalance?: boolean } = {}
) {
if (mode === 'Send') {
// make sure Continue button is disabled as amount is above balance
if (driver.isAndroid) return; // https://github.com/synonymdev/bitkit-android/issues/611
await elementById('ContinueAmount').waitForEnabled({ reverse: aboveBalance });
} else {
// In receive mode Continue is always enabled
await elementById('ReceiveNumberPadSubmit').waitForEnabled();
}
}

async function makeSureIsBitcoinInput(mode: NumberpadMode) {
Expand Down
96 changes: 44 additions & 52 deletions test/specs/send.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,32 @@ describe('@send - Send', () => {
ciIt('@send_1 - Validates payment data in the manual input', async () => {
await tap('Send');
await handleAndroidAlert('permission_allow_foreground_only_button');
await sleep(5000);
await tap('RecipientManual');

// check validation for empty address
await elementById('AddressContinue').waitForEnabled({ reverse: true });

// check validation for invalid data
await typeAddressAndVerifyContinue({ address: 'test123', reverse: true });
await waitForToast('InvalidAddressToast');

//--- skip due to: https://github.com/synonymdev/bitkit-android/issues/354 ---//
// check validation for invalid address (network mismatch)
const mainnetAddress = 'bc1qnc8at2e2navahnz7lvtl39r4dnfzxv3cc9e7ax';
await typeAddressAndVerifyContinue({ address: mainnetAddress, reverse: true });
await waitForToast('InvalidAddressToast');

// // check validation for invalid address (network mismatch)
// const mainnetAddress = 'bc1qnc8at2e2navahnz7lvtl39r4dnfzxv3cc9e7ax';
// await typeAddressAndVerifyContinue({ address: mainnetAddress, reverse: true })

// // check validation for address when balance is 0
// const address = await rpc.getNewAddress();
// console.info({ address });
// await typeAddressAndVerifyContinue({ address: address, reverse: true })

// // check validation for expired invoice
// const invoice =
// 'lnbcrt1pn3zpqpdqqnp4qfh2x8nyvvzq4kf8j9wcaau2chr580l93pnyrh5027l8f7qtm48h6pp5lmwkulnpze4ek4zqwfepguahcr2ma3vfhwa6uepxfd378xlldprssp5wnq34d553g50suuvfy387csx5hx6mdv8zezem6f4tky7rhezycas9qyysgqcqpcxqrrssrzjqtr7pzpunxgwjddwdqucegdphm6776xcarz60gw9gxva0rhal5ntmapyqqqqqqqqpqqqqqlgqqqqqqgq2ql9zpeakxvff9cz5rd6ssc3cngl256u8htm860qv3r28vqkwy9xe3wp0l9ms3zcqvys95yf3r34ytmegz6zynuthh5s0kh7cueunm3mspg3uwpt';
// await typeAddressAndVerifyContinue({ address: invoice, reverse: true })
// check validation for address when balance is 0
const address = await rpc.getNewAddress();
console.info({ address });
await typeAddressAndVerifyContinue({ address: address, reverse: true });
await waitForToast('InsufficientSavingsToast');

//--- skip due to: https://github.com/synonymdev/bitkit-android/issues/354 ---//
// check validation for expired invoice
const invoice =
'lnbcrt1pn3zpqpdqqnp4qfh2x8nyvvzq4kf8j9wcaau2chr580l93pnyrh5027l8f7qtm48h6pp5lmwkulnpze4ek4zqwfepguahcr2ma3vfhwa6uepxfd378xlldprssp5wnq34d553g50suuvfy387csx5hx6mdv8zezem6f4tky7rhezycas9qyysgqcqpcxqrrssrzjqtr7pzpunxgwjddwdqucegdphm6776xcarz60gw9gxva0rhal5ntmapyqqqqqqqqpqqqqqlgqqqqqqgq2ql9zpeakxvff9cz5rd6ssc3cngl256u8htm860qv3r28vqkwy9xe3wp0l9ms3zcqvys95yf3r34ytmegz6zynuthh5s0kh7cueunm3mspg3uwpt';
await typeAddressAndVerifyContinue({ address: invoice, reverse: true });
await waitForToast('ExpiredLightningToast');

// Receive funds and check validation w/ balance
await swipeFullScreen('down');
Expand Down Expand Up @@ -120,13 +121,10 @@ describe('@send - Send', () => {
await typeAddressAndVerifyContinue({ address: unified1 });
}

//--- skip due to: https://github.com/synonymdev/bitkit-android/issues/354 ---//

// // check validation for unified invoice when balance is too low
// const unified2 = 'bitcoin:bcrt1q07x3wl76zdxvdsz3qzzkvxrjg3n6t4tz2vnsx8?amount=0.002';
// await typeAddressAndVerifyContinue({ address: unified2, reverse: true });

//--- skip due to: https://github.com/synonymdev/bitkit-android/issues/354 ---//
// check validation for unified invoice when balance is too low
const unified2 = 'bitcoin:bcrt1q07x3wl76zdxvdsz3qzzkvxrjg3n6t4tz2vnsx8?amount=0.002';
await typeAddressAndVerifyContinue({ address: unified2, reverse: true });
await waitForToast('InsufficientSavingsToast');
});

ciIt('@send_2 - Can receive funds and send to different invoices', async () => {
Expand Down Expand Up @@ -328,37 +326,31 @@ describe('@send - Send', () => {

// send to unified invoice w/ expired invoice
let amtAfterUnified3: string;
if (driver.isAndroid) {
console.info('Sending to unified invoice w/ expired invoice...');
const unified3 =
'bitcoin:bcrt1qaytrqsrgg75rtxrtr7ur6k75la8p3v95mey48z?lightning=LNBCRT1PN33T20DQQNP4QTNTQ4D2DHDYQ420HAUQF5TS7X32TNW9WGYEPQZQ6R9G69QPHW4RXPP5QU7UYXJYJA9PJV7H6JPEYEFFNZ98N686JDEAAK8AUD5AGC5X70HQSP54V5LEFATCQDEU8TLKAF6MDK3ZLU6MWUA52J4JEMD5XA85KGKMTTQ9QYYSGQCQPCXQRRSSRZJQWU6G4HMGH26EXXQYPQD8XHVWLARA66PL53V7S9CV2EE808UGDRN4APYQQQQQQQGRCQQQQLGQQQQQQGQ2QX7F74RT5SQE0KEYCU47LYMSVY2LM4QA4KLR65PPSY55M0H4VR8AN7WVM9EFVSPYJ5R8EFGVXTGVATAGFTC372VRJ3HEPSEELFZ7FQFCQ9XDU9X';
console.info({ unified3 });

// const ln =
// 'LNBCRT1PN33T20DQQNP4QTNTQ4D2DHDYQ420HAUQF5TS7X32TNW9WGYEPQZQ6R9G69QPHW4RXPP5QU7UYXJYJA9PJV7H6JPEYEFFNZ98N686JDEAAK8AUD5AGC5X70HQSP54V5LEFATCQDEU8TLKAF6MDK3ZLU6MWUA52J4JEMD5XA85KGKMTTQ9QYYSGQCQPCXQRRSSRZJQWU6G4HMGH26EXXQYPQD8XHVWLARA66PL53V7S9CV2EE808UGDRN4APYQQQQQQQGRCQQQQLGQQQQQQGQ2QX7F74RT5SQE0KEYCU47LYMSVY2LM4QA4KLR65PPSY55M0H4VR8AN7WVM9EFVSPYJ5R8EFGVXTGVATAGFTC372VRJ3HEPSEELFZ7FQFCQ9XDU9X';
// const dec = await lnd.decodePayReq({ payReq: ln });
// console.info(JSON.stringify(dec, null, 2));
console.info('Sending to unified invoice w/ expired invoice...');
const unified3 =
'bitcoin:bcrt1qaytrqsrgg75rtxrtr7ur6k75la8p3v95mey48z?lightning=LNBCRT1PN33T20DQQNP4QTNTQ4D2DHDYQ420HAUQF5TS7X32TNW9WGYEPQZQ6R9G69QPHW4RXPP5QU7UYXJYJA9PJV7H6JPEYEFFNZ98N686JDEAAK8AUD5AGC5X70HQSP54V5LEFATCQDEU8TLKAF6MDK3ZLU6MWUA52J4JEMD5XA85KGKMTTQ9QYYSGQCQPCXQRRSSRZJQWU6G4HMGH26EXXQYPQD8XHVWLARA66PL53V7S9CV2EE808UGDRN4APYQQQQQQQGRCQQQQLGQQQQQQGQ2QX7F74RT5SQE0KEYCU47LYMSVY2LM4QA4KLR65PPSY55M0H4VR8AN7WVM9EFVSPYJ5R8EFGVXTGVATAGFTC372VRJ3HEPSEELFZ7FQFCQ9XDU9X';
console.info({ unified3 });

// const ln =
// 'LNBCRT1PN33T20DQQNP4QTNTQ4D2DHDYQ420HAUQF5TS7X32TNW9WGYEPQZQ6R9G69QPHW4RXPP5QU7UYXJYJA9PJV7H6JPEYEFFNZ98N686JDEAAK8AUD5AGC5X70HQSP54V5LEFATCQDEU8TLKAF6MDK3ZLU6MWUA52J4JEMD5XA85KGKMTTQ9QYYSGQCQPCXQRRSSRZJQWU6G4HMGH26EXXQYPQD8XHVWLARA66PL53V7S9CV2EE808UGDRN4APYQQQQQQQGRCQQQQLGQQQQQQGQ2QX7F74RT5SQE0KEYCU47LYMSVY2LM4QA4KLR65PPSY55M0H4VR8AN7WVM9EFVSPYJ5R8EFGVXTGVATAGFTC372VRJ3HEPSEELFZ7FQFCQ9XDU9X';
// const dec = await lnd.decodePayReq({ payReq: ln });
// console.info(JSON.stringify(dec, null, 2));

await sleep(1000);
await enterAddress(unified3, { acceptCameraPermission: false });
await elementById('AssetButton-savings').waitForDisplayed();
await tap('N1');
await multiTap('N0', 4);
await tap('ContinueAmount');
await reviewAmt.waitForDisplayed();
await expect(reviewAmt).toHaveText('10 000');
await dragOnElement('GRAB', 'right', 0.95);
await elementById('SendSuccess').waitForDisplayed();
await tap('Close');
await expect(totalBalance).not.toHaveText(amtAfterUnified2);
amtAfterUnified3 = await totalBalance.getText();
console.info({ amtAfterUnified3 });
await expectTextWithin('ActivitySpending', '7 000');
} else {
// https://github.com/synonymdev/bitkit-ios/issues/300
console.info('Skipping sending to unified invoice w/ expired invoice on iOS due to /bitkit-ios/issues/300');
amtAfterUnified3 = amtAfterUnified2;
}
await sleep(1000);
await enterAddress(unified3, { acceptCameraPermission: false });
await elementById('AssetButton-savings').waitForDisplayed();
await tap('N1');
await multiTap('N0', 4);
await tap('ContinueAmount');
await reviewAmt.waitForDisplayed();
await expect(reviewAmt).toHaveText('10 000');
await dragOnElement('GRAB', 'right', 0.95);
await elementById('SendSuccess').waitForDisplayed();
await tap('Close');
await expect(totalBalance).not.toHaveText(amtAfterUnified2);
amtAfterUnified3 = await totalBalance.getText();
console.info({ amtAfterUnified3 });
await expectTextWithin('ActivitySpending', '7 000');

// send to unified invoice w/o amount (lightning)
console.info('Sending to unified invoice w/o amount (lightning)...');
Expand Down
2 changes: 1 addition & 1 deletion wdio.conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const config: WebdriverIO.Config = {
// baseUrl: 'http://localhost:8080',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 30000,
waitforTimeout: process.env.CI ? 30000 : 10000,
//
// Default timeout in milliseconds for request
// if browser driver or grid doesn't send response
Expand Down