From b661e9917470f454722ea7a43d9f44e5d1f08a54 Mon Sep 17 00:00:00 2001 From: Kathy Luo Date: Tue, 21 Nov 2023 15:16:53 +0100 Subject: [PATCH] test: re-enable secure send e2e test (#4507) ### Description This PR: - update e2e consts for addresses mapped to a phone number - re-enable secure send tests, with contract-kit and viem - update fund e2e job to also fund the wallet used for these tests (this wallet needs to be verified in order to do phone number lookups, and needs to be different to the default e2e test wallet so that code paths for non-verified addresses can be tested). refactor here and there to make it easier to read. ### Test plan n/a ### Related issues - Fixes RET-854 ### Backwards compatibility Y --- e2e/scripts/check-e2e-wallet-balance.ts | 6 +- e2e/scripts/consts.ts | 2 + e2e/scripts/fund-e2e-accounts.ts | 131 ++++++++++++++--------- e2e/scripts/utils.ts | 14 ++- e2e/src/Send.spec.js | 8 +- e2e/src/usecases/SecureSend.js | 136 +++++++++++++----------- e2e/src/usecases/Send.js | 11 +- e2e/src/utils/consts.js | 4 + e2e/src/utils/utils.js | 15 ++- scripts/fund_e2e_account.sh | 31 ------ 10 files changed, 192 insertions(+), 166 deletions(-) delete mode 100755 scripts/fund_e2e_account.sh diff --git a/e2e/scripts/check-e2e-wallet-balance.ts b/e2e/scripts/check-e2e-wallet-balance.ts index 6b3d39e6df0..2d448961a3c 100644 --- a/e2e/scripts/check-e2e-wallet-balance.ts +++ b/e2e/scripts/check-e2e-wallet-balance.ts @@ -1,7 +1,11 @@ -import { E2E_TEST_WALLET } from './consts' +import { E2E_TEST_WALLET, E2E_TEST_WALLET_SECURE_SEND } from './consts' import { checkBalance, getBalance } from './utils' ;(async () => { console.log(E2E_TEST_WALLET) console.table(await getBalance(E2E_TEST_WALLET)) await checkBalance(E2E_TEST_WALLET) + + console.log(E2E_TEST_WALLET_SECURE_SEND) + console.table(await getBalance(E2E_TEST_WALLET_SECURE_SEND)) + await checkBalance(E2E_TEST_WALLET_SECURE_SEND) })() diff --git a/e2e/scripts/consts.ts b/e2e/scripts/consts.ts index 93982baf142..7fb65ff556f 100644 --- a/e2e/scripts/consts.ts +++ b/e2e/scripts/consts.ts @@ -1,2 +1,4 @@ export const E2E_TEST_WALLET = '0x6131a6d616a4be3737b38988847270a64bc10caa' +export const E2E_TEST_WALLET_SECURE_SEND = '0x86b8f44386cb2d457db79c3dab8cf42f9d8a3fc0' export const E2E_TEST_FAUCET = '0xe5F5363e31351C38ac82DBAdeaD91Fd5a7B08846' +export const REFILL_TOKENS = ['CELO', 'cUSD', 'cEUR'] diff --git a/e2e/scripts/fund-e2e-accounts.ts b/e2e/scripts/fund-e2e-accounts.ts index 2fd13fa492d..40827d60606 100644 --- a/e2e/scripts/fund-e2e-accounts.ts +++ b/e2e/scripts/fund-e2e-accounts.ts @@ -1,7 +1,12 @@ import { newKitFromWeb3, StableToken } from '@celo/contractkit' import dotenv from 'dotenv' import Web3 from 'web3' -import { E2E_TEST_FAUCET, E2E_TEST_WALLET } from './consts' +import { + E2E_TEST_FAUCET, + E2E_TEST_WALLET, + E2E_TEST_WALLET_SECURE_SEND, + REFILL_TOKENS, +} from './consts' import { checkBalance, getBalance } from './utils' dotenv.config({ path: `${__dirname}/../.env` }) @@ -11,10 +16,16 @@ const kit = newKitFromWeb3(web3) const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! ;(async () => { - // Get E2E Test Wallet Balance & Valora Faucet Balance - const receivingBalance = await getBalance(E2E_TEST_WALLET) - const sendingBalance = (await getBalance(E2E_TEST_FAUCET)) ?? {} - console.table(await getBalance(E2E_TEST_FAUCET)) + const walletsToBeFunded = [E2E_TEST_WALLET, E2E_TEST_WALLET_SECURE_SEND] + const walletBalances = await Promise.all(walletsToBeFunded.map(getBalance)) + for (let i = 0; i < walletsToBeFunded.length; i++) { + console.log(`Initial balance for ${walletsToBeFunded[i]}:`) + console.table(walletBalances[i]) + } + + const faucetTokenBalances = (await getBalance(E2E_TEST_FAUCET)) ?? {} + console.log('Initial faucet balance:') + console.table(faucetTokenBalances) // Connect Valora E2E Test Faucet - Private Key Stored in GitHub Secrets kit.connection.addAccount( @@ -30,24 +41,27 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! const ceurExchange = await kit.contracts.getExchange(StableToken.cEUR) // Balance Faucet - let targetTokenRegex = /[cusd|celo|ceur]/gi - let balances = await getBalance(E2E_TEST_FAUCET) - let sum = 0 - const numOfTokens = 3 // Only cusd, celo and ceur - for (let balance in balances) { - console.log(`${balance}: ${balances[balance]}`) - // Sum only the target balances - if (targetTokenRegex.test(balance)) sum += balances[balance] - } + let totalTokenHoldings = 0 // the absolute number of faucet tokens the faucet is holding + Object.entries(faucetTokenBalances).forEach(([tokenSymbol, tokenBalance]) => { + if (REFILL_TOKENS.includes(tokenSymbol)) { + totalTokenHoldings += tokenBalance + } + }) + const targetFaucetTokenBalance = totalTokenHoldings / REFILL_TOKENS.length - for (let balance in balances) { - const target = sum / numOfTokens - if (balances[balance] >= sum / numOfTokens) { - const toSell = balances[balance] - target - console.log(toSell) - const amountToExchange = kit.web3.utils.toWei(`${toSell}`, 'ether') - console.log(`${balance} balance higher than ${sum / numOfTokens}`) - switch (balance) { + // Ensure that the faucet has enough balance for each refill tokens + for (const [tokenSymbol, tokenBalance] of Object.entries(faucetTokenBalances)) { + if (!REFILL_TOKENS.includes(tokenSymbol)) { + continue + } + + if (tokenBalance >= targetFaucetTokenBalance) { + const sellAmount = tokenBalance - targetFaucetTokenBalance + const amountToExchange = kit.web3.utils.toWei(`${sellAmount}`, 'ether') + console.log( + `${tokenSymbol} balance is ${tokenBalance}, which is higher than target ${targetFaucetTokenBalance}. Selling ${sellAmount}.` + ) + switch (tokenSymbol) { case 'CELO': try { const celoApproveTx = await celoToken @@ -98,10 +112,12 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! } } } else { - const toBuy = target - balances[balance] - const amountToExchange = kit.web3.utils.toWei(`${toBuy}`, 'ether') - console.log(`${balance} balance lower than ${sum / numOfTokens}`) - switch (balance) { + const buyAmount = targetFaucetTokenBalance - tokenBalance + const amountToExchange = kit.web3.utils.toWei(`${buyAmount}`, 'ether') + console.log( + `${tokenSymbol} balance is ${tokenBalance}, which is lower than target ${targetFaucetTokenBalance}. Buying ${buyAmount}.` + ) + switch (tokenSymbol) { case 'CELO': try { const celoApproveTx = await celoToken @@ -155,42 +171,51 @@ const valoraTestFaucetSecret = process.env['TEST_FAUCET_SECRET']! } // Set Amount To Send - let amountToSend = web3.utils.toWei('100', 'ether') + const amountToSend = '100' + const amountToSendWei = web3.utils.toWei(amountToSend, 'ether') - // Loop through E2E Test Wallet Balance Object - for (const coin in receivingBalance) { - let tx: any - // Add funds if balance is less than 100 add 100 - if (receivingBalance[coin] < 200 && sendingBalance[coin] > 100) { - switch (coin) { - case 'CELO': - tx = await celoToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - case 'cUSD': - tx = await cusdToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - case 'cEUR': - tx = await ceurToken - .transfer(E2E_TEST_WALLET, amountToSend) - .send({ from: E2E_TEST_FAUCET }) - break - } - // Wait for the transactions to be processed - let receipt = await tx.waitReceipt() + for (let i = 0; i < walletsToBeFunded.length; i++) { + const walletAddress = walletsToBeFunded[i] + const walletBalance = walletBalances[i] + for (const tokenSymbol of REFILL_TOKENS) { + if (walletBalance && walletBalance[tokenSymbol] < 200) { + console.log(`Sending ${amountToSend} ${tokenSymbol} to ${walletAddress}`) + let tx: any + switch (tokenSymbol) { + case 'CELO': + tx = await celoToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + case 'cUSD': + tx = await cusdToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + case 'cEUR': + tx = await ceurToken + .transfer(walletAddress, amountToSendWei) + .send({ from: E2E_TEST_FAUCET }) + break + } + const receipt = await tx.waitReceipt() - // Print Receipt - console.log(' Transaction receipt: %o', receipt) + console.log( + `Received tx hash ${receipt.transactionHash} for ${tokenSymbol} transfer to ${walletAddress}` + ) + } } } + console.log('Finished funding wallets.') // Log Balances console.log('E2E Test Account:', E2E_TEST_WALLET) console.table(await getBalance(E2E_TEST_WALLET)) + console.log('E2E Test Account Secure Send:', E2E_TEST_WALLET_SECURE_SEND) + console.table(await getBalance(E2E_TEST_WALLET_SECURE_SEND)) console.log('Valora Test Facuet:', E2E_TEST_FAUCET) console.table(await getBalance(E2E_TEST_FAUCET)) + await checkBalance(E2E_TEST_WALLET) + await checkBalance(E2E_TEST_WALLET_SECURE_SEND) })() diff --git a/e2e/scripts/utils.ts b/e2e/scripts/utils.ts index cc94f0eb6a0..fff5bff4d3a 100644 --- a/e2e/scripts/utils.ts +++ b/e2e/scripts/utils.ts @@ -1,13 +1,19 @@ +import { REFILL_TOKENS } from './consts' + export const Web3 = require('web3') export const ContractKit = require('@celo/contractkit') export const dotenv = require('dotenv') export const web3 = new Web3('https://alfajores-forno.celo-testnet.org') export const kit = ContractKit.newKitFromWeb3(web3) -export async function checkBalance(address: string, minBalance = 10) { - let balanceObject = await getBalance(address) - for (const balance in balanceObject) { - if (balanceObject[balance as keyof typeof balanceObject] < minBalance) { +export async function checkBalance( + address: string, + minBalance = 10, + tokenSymbols: string[] = REFILL_TOKENS +) { + const balance = (await getBalance(address)) ?? {} + for (const [tokenSymbol, tokenBalance] of Object.entries(balance)) { + if (tokenSymbols.includes(tokenSymbol) && tokenBalance < minBalance) { throw new Error( `${balance} balance of ${address} is below ${minBalance}. Please refill from the faucet https://celo.org/developers/faucet or run ./fund-e2e-accounts.ts if a Valora Dev.` ) diff --git a/e2e/src/Send.spec.js b/e2e/src/Send.spec.js index 2ae52359b24..ebf8975403d 100644 --- a/e2e/src/Send.spec.js +++ b/e2e/src/Send.spec.js @@ -1,13 +1,7 @@ import SecureSend from './usecases/SecureSend' import Send from './usecases/Send' -import { quickOnboarding } from './utils/utils' describe('Given', () => { - beforeAll(async () => { - await quickOnboarding() - }) - describe('Send', Send) - // TODO: unskip this test if we enable CPV in CI - describe.skip('SecureSend cUSD', SecureSend) + describe('SecureSend with CPV', SecureSend) }) diff --git a/e2e/src/usecases/SecureSend.js b/e2e/src/usecases/SecureSend.js index c250bc08e3e..e788e54b409 100644 --- a/e2e/src/usecases/SecureSend.js +++ b/e2e/src/usecases/SecureSend.js @@ -1,86 +1,92 @@ -import { reloadReactNative } from '../utils/retries' +import { + SAMPLE_BACKUP_KEY_VERIFIED, + VERIFIED_PHONE_NUMBER, + SAMPLE_WALLET_ADDRESS_VERIFIED_2, +} from '../utils/consts' +import { launchApp } from '../utils/retries' import { addComment, enterPinUiIfNecessary, inputNumberKeypad, scrollIntoView, - sleep, + quickOnboarding, } from '../utils/utils' const faker = require('@faker-js/faker') -const PHONE_NUMBER = '+12057368924' -const LAST_ACCOUNT_CHARACTERS = 'FD08' -const AMOUNT_TO_SEND = '0.1' +const AMOUNT_TO_SEND = '0.01' export default SecureSend = () => { - beforeEach(async () => { - await reloadReactNative() - }) - - it('Send cUSD to phone number with multiple mappings', async () => { - let randomContent = faker.lorem.words() - await waitFor(element(by.id('HomeAction-Send'))) - .toBeVisible() - .withTimeout(30000) - await element(by.id('HomeAction-Send')).tap() - - // Look for an address and tap on it. - await element(by.id('SearchInput')).tap() - await element(by.id('SearchInput')).replaceText(PHONE_NUMBER) - await element(by.id('RecipientItem')).tap() + describe.each([{ web3Library: 'contract-kit' }, { web3Library: 'viem' }])( + 'Secure send flow with phone number lookup (with $web3Library)', + ({ web3Library }) => { + beforeAll(async () => { + // uninstall the app to remove secure send mapping + await device.uninstallApp() + await device.installApp() + await launchApp({ + newInstance: true, + permissions: { notifications: 'YES', contacts: 'YES' }, + launchArgs: { + statsigGateOverrides: `use_new_send_flow=false,use_viem_for_send=${ + web3Library === 'viem' + }`, + }, + }) + await quickOnboarding(SAMPLE_BACKUP_KEY_VERIFIED) + }) - // Select the currency - await waitFor(element(by.id('cUSDBalance'))) - .toBeVisible() - .withTimeout(30 * 1000) - await element(by.id('cUSDBalance')).tap() + it('Send cUSD to phone number with multiple mappings', async () => { + let randomContent = faker.lorem.words() + await waitFor(element(by.id('HomeAction-Send'))) + .toBeVisible() + .withTimeout(30000) + await element(by.id('HomeAction-Send')).tap() + await waitFor(element(by.id('SendSearchInput'))).toBeVisible() - // Enter the amount and review - await inputNumberKeypad(AMOUNT_TO_SEND) - await element(by.id('Review')).tap() + await element(by.id('SearchInput')).tap() + await element(by.id('SearchInput')).replaceText(VERIFIED_PHONE_NUMBER) + await element(by.id('RecipientItem')).tap() - // hack: we shouldn't need this but the test fails without - await sleep(3000) + // Select the currency + await waitFor(element(by.id('cUSDTouchable'))).toBeVisible() + await element(by.id('cUSDTouchable')).tap() - // Click Edit if confirm account isn't served - try { - await element(by.id('accountEditButton')).tap() - } catch {} + // Enter the amount and review + await inputNumberKeypad(AMOUNT_TO_SEND) + await element(by.id('Review')).tap() - // Use the last digits of the account to confirm the sender. - await waitFor(element(by.id('confirmAccountButton'))) - .toBeVisible() - .withTimeout(30000) - await element(by.id('confirmAccountButton')).tap() - for (let index = 0; index < 4; index++) { - const character = LAST_ACCOUNT_CHARACTERS[index] - await element(by.id(`SingleDigitInput/digit${index}`)).replaceText(character) - } - - // Scroll to see submit button - await scrollIntoView('Submit', 'KeyboardAwareScrollView', 50) - await element(by.id('ConfirmAccountButton')).tap() + // Use the last digits of the account to confirm the sender. + await waitFor(element(by.id('confirmAccountButton'))).toBeVisible() + await element(by.id('confirmAccountButton')).tap() + // TODO: test case for AddressValidationType.PARTIAL but relies on mapping phone number to another address with unique last 4 digits + // for (let index = 0; index < 4; index++) { + // const character = LAST_ACCOUNT_CHARACTERS[index] + // await element(by.id(`SingleDigitInput/digit${index}`)).replaceText(character) + // } + await element(by.id('ValidateRecipientAccount/TextInput')).replaceText( + SAMPLE_WALLET_ADDRESS_VERIFIED_2 + ) - // Write a comment. - await addComment(randomContent) + // Scroll to see submit button + await scrollIntoView('Submit', 'KeyboardAwareScrollView', 50) + await element(by.id('ConfirmAccountButton')).tap() - // Wait for the confirm button to be clickable. If it takes too long this test - // will be flaky :( - await sleep(3000) + // Write a comment. + await addComment(randomContent) - // Confirm and input PIN if necessary. - await element(by.id('ConfirmButton')).tap() - await enterPinUiIfNecessary() + // Confirm and input PIN if necessary. + await element(by.id('ConfirmButton')).tap() + await enterPinUiIfNecessary() - // Return to home screen. - await waitFor(element(by.id('HomeAction-Send'))) - .toBeVisible() - .withTimeout(30 * 1000) + // Return to home screen. + await waitFor(element(by.id('HomeAction-Send'))) + .toBeVisible() + .withTimeout(30 * 1000) - // TODO: See why these are taking so long in e2e tests to appear - // Look for the latest transaction and assert - // await waitFor(element(by.text(`${randomContent}`))) - // .toBeVisible() - // .withTimeout(60000) - }) + await waitFor(element(by.text(`${randomContent}`))) + .toBeVisible() + .withTimeout(60000) + }) + } + ) } diff --git a/e2e/src/usecases/Send.js b/e2e/src/usecases/Send.js index f2731884762..0bb978c7072 100644 --- a/e2e/src/usecases/Send.js +++ b/e2e/src/usecases/Send.js @@ -1,10 +1,19 @@ import { DEFAULT_RECIPIENT_ADDRESS } from '../utils/consts' import { launchApp, reloadReactNative } from '../utils/retries' -import { enterPinUiIfNecessary, inputNumberKeypad, addComment } from '../utils/utils' +import { + enterPinUiIfNecessary, + inputNumberKeypad, + addComment, + quickOnboarding, +} from '../utils/utils' const faker = require('@faker-js/faker') import jestExpect from 'expect' export default Send = () => { + beforeAll(async () => { + await quickOnboarding() + }) + let randomComment = faker.lorem.words() describe.each([{ web3Library: 'contract-kit' }, { web3Library: 'viem' }])( 'When multi-token send flow (with $web3Library)', diff --git a/e2e/src/utils/consts.js b/e2e/src/utils/consts.js index 1ee7f6e0a26..ae33024e1b9 100644 --- a/e2e/src/utils/consts.js +++ b/e2e/src/utils/consts.js @@ -7,6 +7,10 @@ export const SAMPLE_WALLET_ADDRESS = '0x6131a6d616a4be3737b38988847270a64bc10caa export const SAMPLE_BACKUP_KEY_VERIFIED = 'embody siege middle glory soda solar nasty report swap now never any' export const SAMPLE_WALLET_ADDRESS_VERIFIED = '0x86b8f44386cb2d457db79c3dab8cf42f9d8a3fc0' // corresponds to the backup key above +export const SAMPLE_BACKUP_KEY_VERIFIED_2 = + 'bench album relax truth pond orchard diet unaware cloud tackle twin tongue' +export const SAMPLE_WALLET_ADDRESS_VERIFIED_2 = '0x5fe1407f47b1310ff232a8d368b36099eff61604' // corresponds to the backup key above +export const VERIFIED_PHONE_NUMBER = '+15203140983' // all verified addresses are using this number export const SAMPLE_BACKUP_KEY_12_WORDS = 'cheese size muscle either false spend price lunar undo share kite whisper' export const SAMPLE_WALLET_ADDRESS_12_WORDS = '0x19cf37810fd5933bd63e02388e37203841c703de' // corresponds to the backup key above diff --git a/e2e/src/utils/utils.js b/e2e/src/utils/utils.js index fefa17f0510..338ad6bfb7c 100644 --- a/e2e/src/utils/utils.js +++ b/e2e/src/utils/utils.js @@ -201,10 +201,17 @@ export async function quickOnboarding(mnemonic = SAMPLE_BACKUP_KEY) { await element(by.id('ConfirmUseAccountDialog/PrimaryAction')).tap() } catch {} - // Verify Education - await waitForElementId('PhoneVerificationSkipHeader') - // Skip - await element(by.id('PhoneVerificationSkipHeader')).tap() + // this onboarding step is bypassed for already verified wallets + try { + // Verify Education + await waitForElementId('PhoneVerificationSkipHeader') + // Skip + await element(by.id('PhoneVerificationSkipHeader')).tap() + } catch { + console.log( + 'Error trying to skip phone verification step during onboarding, likely due to wallet already being verified' + ) + } // Assert on Wallet Home Screen await dismissCashInBottomSheet() diff --git a/scripts/fund_e2e_account.sh b/scripts/fund_e2e_account.sh deleted file mode 100755 index eaca1f13484..00000000000 --- a/scripts/fund_e2e_account.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env sh -set -euo pipefail - -CURRENT_CONFIG=$(celocli config:get | awk '{print $2 }') -celocli config:set -n https://alfajores-forno.celo-testnet.org - -TEST_ACCOUNT=0x6131a6d616a4be3737b38988847270a64bc10caa - -echo "Verifying balancess of test account: $TEST_ACCOUNT" - -celocli account:balance $TEST_ACCOUNT >> balance.tmp -WEI_DECIMALS=$((10**18)) -printf -v USD_BALANCE "%.f" "$(grep usd balance.tmp | awk -v wei_decimals="$WEI_DECIMALS" '{print $2 / wei_decimals}')" -printf -v CELO_BALANCE "%.f" "$(grep gold balance.tmp | awk -v wei_decimals="$WEI_DECIMALS" '{print $2 / wei_decimals}')" - -echo "cUSD balance: $USD_BALANCE" -echo "CELO balance: $CELO_BALANCE" - -if [ "$USD_BALANCE" -lt "200" ]; then - echo "Adding USD to test account" - celotooljs account faucet --celo-env alfajores --account $TEST_ACCOUNT --dollar 1000 --yesreally -fi -if [ "$CELO_BALANCE" -lt "200" ]; then - echo "Adding CELO to test account" - celotooljs account faucet --celo-env alfajores --account $TEST_ACCOUNT --gold 1000 --yesreally -fi - -echo "Restoring celocli to use $CURRENT_CONFIG" -celocli config:set -n $CURRENT_CONFIG - -rm balance.tmp