e2e: wallet tests added

This commit is contained in:
Yevheniia Berdnyk 2024-04-02 17:59:56 +03:00
parent e1177d20b5
commit 5bff89a58f
No known key found for this signature in database
GPG Key ID: 0642C73C66214825
10 changed files with 792 additions and 419 deletions

View File

@ -1,59 +1,53 @@
import logging import logging
import time
from decimal import Decimal
from json import JSONDecodeError
from os import environ
from typing import List from typing import List
import pytest import pytest
import requests import requests
import time from selenium.common import TimeoutException
from json import JSONDecodeError
from decimal import Decimal
from os import environ
import tests import tests
class NetworkApi(object): class NetworkApi:
def __init__(self): def __init__(self):
self.network_url = 'http://api-goerli.etherscan.io/api?' self.network_url = 'http://api-sepolia.etherscan.io/api'
self.headers = {
'User-Agent':"Mozilla\\5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit\\537.36 (KHTML, like Gecko) Chrome\\7"
"7.0.3865.90 Safari\\537.36", }
self.chat_bot_url = 'http://offsite.chat:8099'
self.api_key = environ.get('ETHERSCAN_API_KEY') self.api_key = environ.get('ETHERSCAN_API_KEY')
def log(self, text: str): def log(self, text: str):
tests.test_suite_data.current_test.testruns[-1].steps.append(text) tests.test_suite_data.current_test.testruns[-1].steps.append(text)
logging.info(text) logging.info(text)
def send_etherscan_request(self, method, extracted_param: str): def send_etherscan_request(self, params):
for attempt in range(3): params['apikey'] = self.api_key
try: try:
response = requests.request('GET', url=method, headers=self.headers).json() response = requests.get(url=self.network_url, params=params).json()
if response: if response:
return response[extracted_param] return response['result']
except TypeError as e: except TypeError as e:
self.log("Check response from etherscan API. Returned values do not match expected. %s" % str(e)) self.log("Check response from etherscan API. Returned values do not match expected. %s" % str(e))
except JSONDecodeError as e: except JSONDecodeError as e:
self.log("No valid JSON response from Etherscan: %s " % str(e)) self.log("No valid JSON response from Etherscan: %s " % str(e))
pass pass
time.sleep(30)
def get_token_transactions(self, address: str) -> List[dict]: def get_token_transactions(self, address: str) -> List[dict]:
method = self.network_url + 'module=account&action=tokentx&address=0x%s&sort=desc&apikey=%s' % ( params = {'module': 'account', 'action': 'tokentx', 'address': address, 'sort': 'desc'}
address, self.api_key) return self.send_etherscan_request(params)
return self.send_etherscan_request(method, 'result')
def get_transactions(self, address: str) -> List[dict]: def get_transactions(self, address: str) -> List[dict]:
method = self.network_url + 'module=account&action=txlist&address=0x%s&sort=desc&apikey=%s' % (address, self.api_key) params = {'module': 'account', 'action': 'txlist', 'address': address, 'sort': 'desc'}
return self.send_etherscan_request(method, 'result') return self.send_etherscan_request(params)
def is_transaction_successful(self, transaction_hash: str) -> int: def is_transaction_successful(self, transaction_hash: str) -> int:
method = self.network_url + 'module=transaction&action=getstatus&txhash=%s' % transaction_hash params = {'module': 'transaction', 'action': 'getstatus', 'txhash': transaction_hash}
return not int(requests.request('GET', url=method, headers=self.headers).json()['result']['isError']) return not int(self.send_etherscan_request(params)['isError'])
def get_balance(self, address): def get_balance(self, address: str):
address = '0x' + address params = {'module': 'account', 'action': 'balance', 'address': address, 'tag': 'latest'}
method = self.network_url + 'module=account&action=balance&address=%s&tag=latest&apikey=%s' % ( balance = self.send_etherscan_request(params)
address, self.api_key)
balance = self.send_etherscan_request(method, 'result')
if balance: if balance:
self.log('Balance is %s Gwei' % balance) self.log('Balance is %s Gwei' % balance)
return int(balance) return int(balance)
@ -61,13 +55,12 @@ class NetworkApi(object):
self.log('Cannot extract balance!') self.log('Cannot extract balance!')
def get_latest_block_number(self) -> int: def get_latest_block_number(self) -> int:
method = self.network_url + 'module=proxy&action=eth_blockNumber' params = {'module': 'proxy', 'action': 'eth_blockNumber'}
return int(requests.request('GET', url=method).json()['result'], 0) return int(self.send_etherscan_request(params), 0)
def find_transaction_by_hash(self, transaction_hash: str): def find_transaction_by_hash(self, transaction_hash: str):
method = self.network_url + 'module=transaction&action=gettxreceiptstatus&txhash=%s&apikey=%s' % ( params = {'module': 'transaction', 'action': 'gettxreceiptstatus', 'txhash': transaction_hash}
transaction_hash, self.api_key) result = self.send_etherscan_request(params)
result = self.send_etherscan_request(method, 'result')
if result: if result:
final_status = True final_status = True
@ -86,13 +79,14 @@ class NetworkApi(object):
while True: while True:
if counter >= wait_time: if counter >= wait_time:
for entry in range(0, 5): for entry in range(0, 5):
self.log('Transaction #%s, amount is %s' %(entry+1, float(int(transactions[entry]['value']) / 10 ** decimals))) self.log('Transaction #%s, amount is %s' % (
entry + 1, float(int(transactions[entry]['value']) / 10 ** decimals)))
self.log(str(transactions[entry])) self.log(str(transactions[entry]))
pytest.fail( pytest.fail(
'Transaction with amount %s is not found in list of %s, address is %s during %ss' % 'Transaction with amount %s is not found in list of %s, address is %s during %ss' %
(amount, additional_info, address, wait_time)) (amount, additional_info, address, wait_time))
else: else:
self.log("Finding tx in %s, attempt #%s" % (additional_info, str(int(counter / 30)+1))) self.log("Finding tx in %s, attempt #%s" % (additional_info, str(int(counter / 30) + 1)))
try: try:
if token: if token:
transactions = self.get_token_transactions(address) transactions = self.get_token_transactions(address)
@ -126,7 +120,8 @@ class NetworkApi(object):
if int(transaction['confirmations']) >= confirmations: if int(transaction['confirmations']) >= confirmations:
return return
time.sleep(20) time.sleep(20)
pytest.fail('Transaction with amount %s was not confirmed, address is %s, still has %s confirmations' % (amount, address, int(transaction['confirmations']))) pytest.fail('Transaction with amount %s was not confirmed, address is %s, still has %s confirmations' % (
amount, address, int(transaction['confirmations'])))
def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=360): def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=360):
counter = 0 counter = 0
@ -141,10 +136,13 @@ class NetworkApi(object):
self.log('Balance is updated!') self.log('Balance is updated!')
return return
def verify_balance_is(self, expected_balance: int, recipient_address: str, errors: list): def wait_for_balance_to_be(self, address: str, expected_balance: int, less: bool = True):
balance = self.get_balance(recipient_address) for _ in range(5):
if balance / 1000000000000000000 != expected_balance: balance = self.get_balance(address) / 1000000000000000000
errors.append('Recipients balance is not updated on etherscan') if (less and balance < expected_balance) or (not less and balance > expected_balance):
return
time.sleep(10)
raise TimeoutException('Balance is not updated on Etherscan')
# Do not use until web3 update # Do not use until web3 update
# def faucet(self, address): # def faucet(self, address):
@ -160,7 +158,6 @@ class NetworkApi(object):
# address = "0x" + address # address = "0x" + address
# w3.donate_testnet_eth(address=address, amount=0.01, inscrease_default_gas_price=10) # w3.donate_testnet_eth(address=address, amount=0.01, inscrease_default_gas_price=10)
# def get_donate(self, address, external_faucet=False, wait_time=300): # def get_donate(self, address, external_faucet=False, wait_time=300):
# initial_balance = self.get_balance(address) # initial_balance = self.get_balance(address)
# counter = 0 # counter = 0
@ -180,11 +177,6 @@ class NetworkApi(object):
# self.log('Got %s Gwei for %s' % (self.get_balance(address), address)) # self.log('Got %s Gwei for %s' % (self.get_balance(address), address))
# return # return
def start_chat_bot(self, chat_name: str, messages_number: int, interval: int = 1) -> list:
url = '%s/ping/%s?count=%s&interval=%s' % (self.chat_bot_url, chat_name, messages_number, interval)
text = requests.request('GET', url).text
return [i.split(maxsplit=5)[-1].strip('*') for i in text.splitlines()]
def get_rounded_balance(self, fetched_balance, actual_balance): def get_rounded_balance(self, fetched_balance, actual_balance):
fetched_balance, actual_balance = str(fetched_balance), str(actual_balance) fetched_balance, actual_balance = str(fetched_balance), str(actual_balance)
# get actual number of decimals on account balance # get actual number of decimals on account balance
@ -192,15 +184,14 @@ class NetworkApi(object):
rounded_balance = round(float(actual_balance), decimals) rounded_balance = round(float(actual_balance), decimals)
return rounded_balance return rounded_balance
def get_tx_param_by_hash(self, hash: str, param: str): def get_tx_param_by_hash(self, transaction_hash: str, param: str):
method = self.network_url + 'module=proxy&action=eth_getTransactionByHash&txhash=%s&apikey=%s' % ( params = {'module': 'proxy', 'action': 'eth_getTransactionByHash', 'txhash': transaction_hash}
hash, self.api_key) res = self.send_etherscan_request(params)
res = self.send_etherscan_request(method, 'result')
return int(res[param], 16) return int(res[param], 16)
def get_custom_fee_tx_params(self, hash: str): def get_custom_fee_tx_params(self, hash: str):
return { return {
'fee_cap': str(self.get_tx_param_by_hash(hash, 'maxFeePerGas')/1000000000), 'fee_cap': str(self.get_tx_param_by_hash(hash, 'maxFeePerGas') / 1000000000),
'tip_cap': str(self.get_tx_param_by_hash(hash, 'maxPriorityFeePerGas')/1000000000), 'tip_cap': str(self.get_tx_param_by_hash(hash, 'maxPriorityFeePerGas') / 1000000000),
'gas_limit': str(self.get_tx_param_by_hash(hash, 'gas')) 'gas_limit': str(self.get_tx_param_by_hash(hash, 'gas'))
} }

View File

@ -129,6 +129,7 @@ class TestrailReport(BaseTestReport):
test_cases['pr']['community_multiple'] = 50982 test_cases['pr']['community_multiple'] = 50982
test_cases['pr']['activity_centre_contact_request'] = 50984 test_cases['pr']['activity_centre_contact_request'] = 50984
test_cases['pr']['activity_centre_other'] = 51005 test_cases['pr']['activity_centre_other'] = 51005
test_cases['pr']['wallet'] = 59443
## Nightly e2e ## Nightly e2e
# test_cases['nightly']['activity_center'] = 736 # test_cases['nightly']['activity_center'] = 736

View File

@ -0,0 +1,213 @@
import time
import pytest
from _pytest.outcomes import Failed
from selenium.common import TimeoutException
from base_test_case import MultipleSharedDeviceTestCase, create_shared_drivers
from support.api.network_api import NetworkApi
from tests import marks, run_in_parallel
from users import transaction_recipients
from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="new_four_2")
@marks.new_ui_critical
class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.network_api = NetworkApi()
self.drivers, self.loop = create_shared_drivers(2)
self.sign_in_1, self.sign_in_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
self.sender, self.receiver = transaction_recipients['J'], transaction_recipients['K']
self.sender_username, self.receiver_username = 'sender', 'receiver'
self.loop.run_until_complete(
run_in_parallel(((self.sign_in_1.recover_access, {'passphrase': self.sender['passphrase'],
'username': self.sender_username}),
(self.sign_in_2.recover_access, {'passphrase': self.receiver['passphrase'],
'username': self.receiver_username}))))
self.home_1, self.home_2 = self.sign_in_1.get_home_view(), self.sign_in_2.get_home_view()
self.wallet_1, self.wallet_2 = self.sign_in_1.get_wallet_view(), self.sign_in_2.get_wallet_view()
# ToDo: Add verification of Activity tabs when the feature is ready in the next 2 tests:
def _get_balances_before_tx(self):
sender_balance = self.network_api.get_balance(self.sender['address'])
receiver_balance = self.network_api.get_balance(self.receiver['address'])
self.wallet_1.just_fyi("Getting ETH amount in the wallet of the sender before transaction")
self.wallet_1.wallet_tab.click()
self.wallet_1.get_account_element().click()
eth_amount_sender = self.wallet_1.get_asset(asset_name='Ether').get_amount()
self.wallet_2.just_fyi("Getting ETH amount in the wallet of the receiver before transaction")
self.wallet_2.wallet_tab.click()
self.wallet_2.get_account_element().click()
eth_amount_receiver = self.wallet_2.get_asset(asset_name='Ether').get_amount()
return sender_balance, receiver_balance, eth_amount_sender, eth_amount_receiver
def _check_balances_after_tx(self, amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
eth_amount_receiver):
try:
self.network_api.wait_for_balance_to_be(address=self.sender['address'],
expected_balance=sender_balance - amount_to_send)
except TimeoutException:
self.errors.append("Sender balance was not updated")
try:
self.network_api.wait_for_balance_to_be(address=self.receiver['address'],
expected_balance=receiver_balance + amount_to_send)
except TimeoutException:
self.errors.append("Receiver balance was not updated")
def wait_for_wallet_balance_to_update(wallet_view, user_name, initial_eth_amount):
wallet_view.just_fyi("Getting ETH amount in the wallet of the %s after transaction" % user_name)
if user_name == 'sender':
exp_amount = round(initial_eth_amount - amount_to_send, 4)
else:
exp_amount = round(initial_eth_amount + amount_to_send, 4)
# for _ in range(12): # ToDo: 120 sec wait time, enable when autoupdate feature is ready
wallet_view.wallet_tab.click()
new_eth_amount = round(wallet_view.get_asset(asset_name='Ether').get_amount(), 4)
if user_name == 'sender' and new_eth_amount < exp_amount:
return
if user_name == 'receiver' and new_eth_amount >= exp_amount:
return
# wallet_view.chats_tab.click()
# time.sleep(10)
self.errors.append(
"Eth amount in the %ss wallet is %s but should be %s" % (user_name, new_eth_amount, exp_amount))
# ToDo: disable relogin when autoupdate feature ia ready
self.home_1.just_fyi("Relogin for getting an updated balance")
self.home_2.just_fyi("Relogin for getting an updated balance")
for _ in range(6): # just waiting 1 minute here to be sure that balances are updated
self.wallet_1.wallet_tab.click()
self.wallet_2.wallet_tab.click()
time.sleep(10)
self.loop.run_until_complete(
run_in_parallel(((self.home_1.reopen_app,),
(self.home_2.reopen_app,))))
self.loop.run_until_complete(
run_in_parallel(((wait_for_wallet_balance_to_update, {'wallet_view': self.wallet_1,
'user_name': self.sender_username,
'initial_eth_amount': eth_amount_sender}),
(wait_for_wallet_balance_to_update, {'wallet_view': self.wallet_2,
'user_name': self.receiver_username,
'initial_eth_amount': eth_amount_receiver}))))
self.errors.verify_no_errors()
@marks.testrail_id(727229)
def test_wallet_send_eth(self):
sender_balance, receiver_balance, eth_amount_sender, eth_amount_receiver = self._get_balances_before_tx()
self.wallet_2.close_account_button.click()
self.wallet_2.chats_tab.click()
self.wallet_1.just_fyi("Sending funds from wallet")
amount_to_send = 0.0001
self.wallet_1.send_asset(address=self.receiver['address'], asset_name='Ether', amount=amount_to_send)
self.wallet_1.close_account_button.click_until_presence_of_element(self.home_1.show_qr_code_button)
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
eth_amount_receiver)
@marks.testrail_id(727230)
def test_wallet_send_asset_from_drawer(self):
sender_balance, receiver_balance, eth_amount_sender, eth_amount_receiver = self._get_balances_before_tx()
self.wallet_2.close_account_button.click()
self.wallet_2.chats_tab.click()
self.wallet_1.just_fyi("Sending asset from drawer")
amount_to_send = 0.0001
self.wallet_1.send_asset_from_drawer(address=self.receiver['address'], asset_name='Ether',
amount=amount_to_send)
self.wallet_1.close_account_button.click_until_presence_of_element(self.home_1.show_qr_code_button)
self._check_balances_after_tx(amount_to_send, sender_balance, receiver_balance, eth_amount_sender,
eth_amount_receiver)
@pytest.mark.xdist_group(name="new_one_2")
@marks.new_ui_critical
class TestWalletOneDevice(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.network_api = NetworkApi()
self.drivers, self.loop = create_shared_drivers(1)
self.sign_in_view = SignInView(self.drivers[0])
self.sign_in_view.create_user()
self.home_view = self.sign_in_view.get_home_view()
self.wallet_view = self.home_view.wallet_tab.click()
@marks.testrail_id(727231)
def test_wallet_add_remove_regular_account(self):
self.wallet_view.just_fyi("Adding new regular account")
new_account_name = "New Account"
self.wallet_view.add_regular_account(account_name=new_account_name)
if self.wallet_view.account_name_text.text != new_account_name:
pytest.fail("New account is not created")
self.wallet_view.account_emoji_button.click_until_presence_of_element(self.wallet_view.copy_address_button)
self.wallet_view.share_address_button.click()
new_wallet_address = self.wallet_view.sharing_text_native.text
self.wallet_view.click_system_back_button()
self.wallet_view.close_account_button.click_until_presence_of_element(self.home_view.show_qr_code_button)
self.wallet_view.just_fyi("Checking that the new wallet is added to the Sare QR Code menu")
self.home_view.show_qr_code_button.click()
self.home_view.share_wallet_tab_button.click()
if self.home_view.account_name_text.text != 'Account 1':
self.errors.append("Incorrect first account is shown on Share QR Code menu")
self.home_view.qr_code_image_element.swipe_left_on_element()
try:
self.home_view.account_name_text.wait_for_element_text(text=new_account_name, wait_time=3)
if self.home_view.copy_wallet_address() != new_wallet_address.split(':')[-1]:
self.home_view.driver.fail("Incorrect address")
except Failed:
self.errors.append("Can't swipe between accounts, newly added account is not shown")
self.home_view.click_system_back_button()
self.wallet_view.just_fyi("Removing newly added account")
if self.wallet_view.get_account_element(account_name=new_account_name).is_element_displayed():
self.wallet_view.remove_account(account_name=new_account_name)
if self.wallet_view.get_account_element(account_name=new_account_name).is_element_displayed():
self.errors.append("Account was not removed from wallet")
else:
self.errors.append("Newly added account is not shown in the accounts list")
self.errors.verify_no_errors()
@marks.testrail_id(727232)
def test_wallet_add_remove_watch_only_account(self):
self.wallet_view.just_fyi("Adding new watch only account")
new_account_name = "Account to watch"
address_to_watch = "0x8d2413447ff297d30bdc475f6d5cb00254685aae"
self.wallet_view.add_watch_only_account(address=address_to_watch, account_name=new_account_name)
if self.wallet_view.account_name_text.text != new_account_name:
pytest.fail("Account to watch was not added")
self.wallet_view.close_account_button.click_until_presence_of_element(self.home_view.show_qr_code_button)
self.wallet_view.just_fyi("Checking that the new wallet is added to the Sare QR Code menu")
self.home_view.show_qr_code_button.click()
self.home_view.share_wallet_tab_button.click()
if self.home_view.account_name_text.text != 'Account 1':
self.errors.append("Incorrect first account is shown on Share QR Code menu")
self.home_view.qr_code_image_element.swipe_left_on_element()
try:
self.home_view.account_name_text.wait_for_element_text(text=new_account_name, wait_time=3)
if self.home_view.copy_wallet_address() != address_to_watch:
self.home_view.driver.fail("Incorrect address")
except Failed:
self.errors.append("Can't swipe between accounts, account to watch is not shown")
self.home_view.click_system_back_button()
self.wallet_view.just_fyi("Removing account to watch")
if self.wallet_view.get_account_element(account_name=new_account_name).is_element_displayed():
self.wallet_view.remove_account(account_name=new_account_name, watch_only=True)
if self.wallet_view.get_account_element(account_name=new_account_name).is_element_displayed():
self.errors.append("Account was not removed from wallet")
else:
self.errors.append("Watch only account is not shown in the accounts list")
self.errors.verify_no_errors()

View File

@ -190,7 +190,7 @@ class BaseElement(object):
self.driver.fail(message if message else "`%s` is not equal to expected `%s` in %s sec" % ( self.driver.fail(message if message else "`%s` is not equal to expected `%s` in %s sec" % (
element_text, text, wait_time)) element_text, text, wait_time))
def scroll_to_element(self, depth: int = 9, direction='down'): def scroll_to_element(self, depth: int = 9, direction='down', down_start_y=0.4, down_end_y=0.05):
self.driver.info('Scrolling %s to %s' % (direction, self.name)) self.driver.info('Scrolling %s to %s' % (direction, self.name))
for _ in range(depth): for _ in range(depth):
try: try:
@ -198,7 +198,7 @@ class BaseElement(object):
except NoSuchElementException: except NoSuchElementException:
size = self.driver.get_window_size() size = self.driver.get_window_size()
if direction == 'down': if direction == 'down':
self.driver.swipe(500, size["height"] * 0.4, 500, size["height"] * 0.05) self.driver.swipe(500, size["height"] * down_start_y, 500, size["height"] * down_end_y)
else: else:
self.driver.swipe(500, size["height"] * 0.25, 500, size["height"] * 0.8) self.driver.swipe(500, size["height"] * 0.25, 500, size["height"] * 0.8)
else: else:
@ -303,12 +303,13 @@ class BaseElement(object):
width, height = size['width'], size['height'] width, height = size['width'], size['height']
self.driver.swipe(start_x=x + width * 0.75, start_y=y + height / 2, end_x=x, end_y=y + height / 2) self.driver.swipe(start_x=x + width * 0.75, start_y=y + height / 2, end_x=x, end_y=y + height / 2)
def swipe_right_on_element(self, width_percentage=0.9): def swipe_right_on_element(self, width_percentage=0.9, start_x=0):
self.driver.info("Swiping right on element %s" % self.name) self.driver.info("Swiping right on element %s" % self.name)
location, size = self.get_element_coordinates() location, size = self.get_element_coordinates()
x, y = location['x'], location['y'] x, y = location['x'], location['y']
width, height = size['width'], size['height'] width, height = size['width'], size['height']
self.driver.swipe(start_x=x, start_y=y + height / 2, end_x=x + width * width_percentage, end_y=y + height / 2) self.driver.swipe(start_x=x + start_x, start_y=y + height / 2, end_x=x + width * width_percentage,
end_y=y + height / 2)
def swipe_to_web_element(self, depth=700): def swipe_to_web_element(self, depth=700):
element = self.find_element() element = self.find_element()

View File

@ -94,6 +94,10 @@ class WalletTab(TabButton):
def __init__(self, driver): def __init__(self, driver):
super().__init__(driver, accessibility_id="wallet-stack-tab") super().__init__(driver, accessibility_id="wallet-stack-tab")
def navigate(self):
from views.wallet_view import WalletView
return WalletView(self.driver)
class BrowserTab(TabButton): class BrowserTab(TabButton):
def __init__(self, driver): def __init__(self, driver):
@ -128,11 +132,11 @@ class WalletButton(TabButton):
super().__init__(driver, xpath="//*[contains(@content-desc,'tab, 3 out of 5')]") super().__init__(driver, xpath="//*[contains(@content-desc,'tab, 3 out of 5')]")
def navigate(self): def navigate(self):
from views.wallet_view import WalletView from views.wallet_view_old_ui import WalletView
return WalletView(self.driver) return WalletView(self.driver)
def click(self): def click(self):
from views.wallet_view import WalletView from views.wallet_view_old_ui import WalletView
self.click_until_presence_of_element(WalletView(self.driver).multiaccount_more_options) self.click_until_presence_of_element(WalletView(self.driver).multiaccount_more_options)
return self.navigate() return self.navigate()
@ -245,6 +249,14 @@ class SignInPhraseText(Text):
return self.text.split() return self.text.split()
class SlideButton(Button):
def __init__(self, driver):
super().__init__(driver, xpath="//*[@resource-id='slide-button-track']")
def slide(self):
self.swipe_right_on_element(width_percentage=1.3, start_x=100)
class BaseView(object): class BaseView(object):
def __init__(self, driver): def __init__(self, driver):
self.driver = driver self.driver = driver
@ -318,6 +330,7 @@ class BaseView(object):
# checkboxes and toggles # checkboxes and toggles
self.checkbox_button = CheckBox(self.driver, accessibility_id="checkbox-off") self.checkbox_button = CheckBox(self.driver, accessibility_id="checkbox-off")
self.slide_button_track = SlideButton(self.driver)
# external browser # external browser
self.open_in_status_button = OpenInStatusButton(self.driver) self.open_in_status_button = OpenInStatusButton(self.driver)

View File

@ -225,6 +225,15 @@ class MuteButton(Button):
return self.find_element().find_element(by=MobileBy.XPATH, value="//android.widget.TextView[2]").text return self.find_element().find_element(by=MobileBy.XPATH, value="//android.widget.TextView[2]").text
class ShareQRCodeInfoText(Text):
def __init__(self, driver):
super().__init__(driver, accessibility_id="share-qr-code-info-text")
@property
def text(self):
return self.find_element().find_element(by=MobileBy.XPATH, value="/android.widget.TextView").text
class HomeView(BaseView): class HomeView(BaseView):
def __init__(self, driver): def __init__(self, driver):
super().__init__(driver) super().__init__(driver)
@ -329,10 +338,16 @@ class HomeView(BaseView):
self.mark_all_read_activity_button = Button(self.driver, translation_id="mark-all-notifications-as-read") self.mark_all_read_activity_button = Button(self.driver, translation_id="mark-all-notifications-as-read")
# Share tab # Share tab
self.share_qr_code_info_text = ShareQRCodeInfoText(self.driver)
self.link_to_profile_button = Button(self.driver, accessibility_id="link-to-profile") self.link_to_profile_button = Button(self.driver, accessibility_id="link-to-profile")
self.link_to_profile_text = Text(self.driver, accessibility_id="share-qr-code-info-text") self.link_to_profile_text = Text(self.driver, accessibility_id="share-qr-code-info-text")
self.close_share_tab_button = Button(self.driver, accessibility_id="close-shell-share-tab") self.close_share_tab_button = Button(self.driver, accessibility_id="close-shell-share-tab")
self.qr_code_image_element = BaseElement(self.driver, accessibility_id='share-qr-code')
self.share_wallet_tab_button = Button(self.driver, accessibility_id="Wallet")
self.account_avatar = BaseElement(self.driver, accessibility_id="account-avatar")
self.account_name_text = Text(
self.driver, xpath="//*[@content-desc='link-to-profile']/preceding-sibling::android.widget.TextView")
self.share_link_to_profile_button = Button(self.driver, accessibility_id='link-to-profile')
# Discover communities # Discover communities
self.community_card_item = BaseElement(self.driver, accessibility_id="community-card-item") self.community_card_item = BaseElement(self.driver, accessibility_id="community-card-item")
@ -571,3 +586,17 @@ class HomeView(BaseView):
link_to_profile = self.get_link_to_profile() link_to_profile = self.get_link_to_profile()
self.click_system_back_button() self.click_system_back_button()
return link_to_profile.split("#")[-1] return link_to_profile.split("#")[-1]
def copy_wallet_address(self):
self.share_link_to_profile_button.click()
address = self.sharing_text_native.text
self.click_system_back_button()
return address
def get_wallet_address(self):
self.show_qr_code_button.click()
self.share_wallet_tab_button.click()
self.account_avatar.wait_for_visibility_of_element()
address = self.copy_wallet_address()
self.click_system_back_button()
return address

View File

@ -292,7 +292,6 @@ class ProfileView(BaseView):
self.syncing_button = Button(self.driver, accessibility_id="icon, Syncing, label-component, icon") self.syncing_button = Button(self.driver, accessibility_id="icon, Syncing, label-component, icon")
self.sync_plus_button = Button(self.driver, self.sync_plus_button = Button(self.driver,
xpath="//*[@text='Syncing']/following-sibling::android.view.ViewGroup[1]") xpath="//*[@text='Syncing']/following-sibling::android.view.ViewGroup[1]")
self.slide_button_track = Button(self.driver, xpath="//*[@resource-id='slide-button-track']")
# Keycard # Keycard
self.keycard_button = Button(self.driver, accessibility_id="keycard-button") self.keycard_button = Button(self.driver, accessibility_id="keycard-button")

View File

@ -1,378 +1,126 @@
import time import pytest
from tests import common_password from tests import common_password
from views.base_element import Button, Text, EditBox, SilentButton, CheckBox from views.base_element import Button, EditBox, Text
from views.base_view import BaseView from views.base_view import BaseView
from views.home_view import HomeView
from views.sign_in_view import SignInView
class TransactionHistoryButton(Button): class AssetElement(Button):
def __init__(self, driver):
super().__init__(driver, accessibility_id="History-item-button")
def navigate(self):
from views.transactions_view import TransactionsView
return TransactionsView(self.driver)
class AssetCheckBox(CheckBox):
def __init__(self, driver, asset_name): def __init__(self, driver, asset_name):
super().__init__(driver, xpath="//*[@text='%s']" % asset_name) self.asset_name = asset_name
self.locator = "//android.view.ViewGroup[@content-desc='container']/android.widget.TextView[@text='%s']" % \
self.asset_name
super().__init__(driver=driver, xpath=self.locator)
def enable(self): def get_amount(self):
self.scroll_to_element(12) element = Text(self.driver, xpath=self.locator + "/../android.widget.TextView[3]")
super().enable() element.scroll_to_element(down_start_y=0.89, down_end_y=0.8)
try:
amount = element.text.split()[0]
class BackupRecoveryPhrase(Button): if '<' in amount:
def __init__(self, driver): return 0
super().__init__(driver, translation_id="wallet-backup-recovery-title") else:
return float(amount)
def navigate(self): except ValueError:
from views.profile_view import ProfileView pytest.fail("Cannot get %s amount" % self.asset_name)
return ProfileView(self.driver)
class AccountElementButton(SilentButton):
def __init__(self, driver, account_name):
super().__init__(driver, xpath="//*[@content-desc='accountcard%s']" % account_name)
def color_matches(self, expected_color_image_name: str):
amount_text = Text(self.driver, xpath="%s//*[@content-desc='account-total-value']" % self.locator)
amount_text.wait_for_element_text('...', 60)
return not amount_text.is_element_differs_from_template(expected_color_image_name)
class SendTransactionButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="wallet-send")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class SendTransactionFromMainButton(Button):
def __init__(self, driver):
super().__init__(driver, accessibility_id="send-transaction-button")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class ReceiveTransactionButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="receive")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class AddCustomTokenButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="add-custom-token")
def navigate(self):
from views.add_custom_token_view import AddCustomTokenView
return AddCustomTokenView(self.driver)
class AccountColorButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="account-color", suffix="/following-sibling::android.view.ViewGroup[1]")
def select_color_by_position(self, position: int):
self.click()
self.driver.find_element_by_xpath(
"((//android.widget.ScrollView)[1]/*/*)[%s]" % str(position + 1)).click()
class WalletView(BaseView): class WalletView(BaseView):
def __init__(self, driver): def __init__(self, driver):
super().__init__(driver) super().__init__(driver)
# Wallet view
self.collectibles_tab = Button(self.driver, accessibility_id='Collectibles')
self.add_account_button = Button(self.driver, accessibility_id='add-account')
self.send_transaction_button = SendTransactionButton(self.driver) # Account adding
self.send_transaction_from_main_screen = SendTransactionFromMainButton(self.driver) # ToDo: add unique accessibility ids for the next 2 elements:
self.transaction_history_button = TransactionHistoryButton(self.driver) self.create_account_button = HomeView(self.driver).start_a_new_chat_bottom_sheet_button
self.usd_total_value = Text(self.driver, accessibility_id="total-amount-value-text") self.add_account_to_watch = HomeView(self.driver).add_a_contact_chat_bottom_sheet_button
self.address_to_watch_input = EditBox(self.driver, accessibility_id='add-address-to-watch')
self.account_has_activity_label = Text(self.driver, accessibility_id='account-has-activity')
self.add_account_continue_button = Button(self.driver, accessibility_id='Continue')
self.add_watched_address_button = Button(self.driver, accessibility_id='confirm-button-label')
self.receive_transaction_button = ReceiveTransactionButton(self.driver) # Account view
self.options_button = Button(self.driver, accessibility_id="options-menu-button") self.close_account_button = Button(self.driver, accessibility_id='top-bar')
self.manage_assets_button = Button(self.driver, accessibility_id="wallet-manage-assets") self.account_name_text = Text(
self.manage_accounts_button = Button(self.driver, accessibility_id="wallet-manage-accounts") self.driver, xpath="//*[@content-desc='account-avatar']/../following-sibling::android.widget.TextView[1]")
self.scan_tokens_button = Button(self.driver, accessibility_id="wallet-scan-token") self.account_emoji_button = Button(self.driver, accessibility_id='account-emoji')
self.all_assets_full_names = Text(self.driver, self.send_button = Button(self.driver, accessibility_id='send')
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[1]") self.copy_address_button = Button(self.driver, accessibility_id='copy-address')
self.all_assets_symbols = Button(self.driver, self.share_address_button = Button(self.driver, accessibility_id='share-account')
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[2]") self.remove_account_button = Button(self.driver, accessibility_id='remove-account')
self.currency_item_text = Text(self.driver, xpath="//*[@content-desc='currency-item']//android.widget.TextView") self.derivation_path_note_checkbox = Button(self.driver, accessibility_id='checkbox-off')
self.address_text = Text(self.driver, accessibility_id="address-text") # Sending transaction
self.address_text_input = EditBox(self.driver, accessibility_id='address-text-input')
self.continue_button = Button(self.driver, accessibility_id='continue-button')
self.amount_input = EditBox(self.driver, xpath="//android.widget.EditText")
self.confirm_button = Button(self.driver, accessibility_id='button-one')
self.done_button = Button(self.driver, accessibility_id='done')
self.remind_me_later_button = Button(self.driver, translation_id="remind-me-later") def get_account_element(self, account_name: str = 'Account 1'):
return Button(self.driver, xpath="//android.view.ViewGroup[contains(@content-desc,'%s')]" % account_name)
self.total_amount_text = Text(self.driver, accessibility_id="total-amount-value-text") def get_asset(self, asset_name: str):
self.currency_text = Text(self.driver, accessibility_id="total-amount-currency-text") element = AssetElement(driver=self.driver, asset_name=asset_name)
self.backup_recovery_phrase = BackupRecoveryPhrase(self.driver) element.scroll_to_element(down_start_y=0.89, down_end_y=0.8)
self.backup_recovery_phrase_warning_text = Text(self.driver, return element
accessibility_id="back-up-your-seed-phrase-warning")
self.add_custom_token_button = AddCustomTokenButton(self.driver) def select_asset(self, asset_name: str):
return Button(driver=self.driver,
xpath="//*[@content-desc='token-network']/android.widget.TextView[@text='%s']" % asset_name)
# elements for multiaccount def slide_and_confirm_with_password(self):
self.multiaccount_more_options = Button(self.driver, accessibility_id="accounts-more-options") self.slide_button_track.slide()
self.accounts_status_account = AccountElementButton(self.driver, account_name=self.status_account_name) self.password_input.send_keys(common_password)
self.set_currency_button = Button(self.driver, translation_id="set-currency") self.login_button.click()
self.add_account_button = Button(self.driver, accessibility_id="add-new-account")
self.generate_an_account_button = Button(self.driver, accessibility_id="add-account-sheet-generate")
self.add_watch_only_address_button = Button(self.driver, accessibility_id="add-account-sheet-watch")
self.enter_a_seed_phrase_button = Button(self.driver, accessibility_id="add-account-sheet-seed")
self.enter_a_private_key_button = Button(self.driver, accessibility_id="add-account-sheet-private-key")
self.enter_address_input = EditBox(self.driver, accessibility_id="add-account-enter-watch-address")
self.enter_seed_phrase_input = EditBox(self.driver, accessibility_id="add-account-enter-seed")
self.enter_a_private_key_input = EditBox(self.driver, accessibility_id="add-account-enter-private-key")
self.delete_account_button = Button(self.driver, translation_id="delete-account")
self.enter_your_password_input = EditBox(self.driver, accessibility_id="add-account-enter-password")
self.account_name_input = EditBox(self.driver, accessibility_id="enter-account-name")
self.account_color_button = AccountColorButton(self.driver)
self.add_account_generate_account_button = Button(self.driver,
accessibility_id="add-account-add-account-button")
self.status_account_total_usd_value = Text(self.driver, accessibility_id="account-total-value")
self.scan_qr_button = Button(self.driver, accessibility_id="accounts-qr-code")
self.close_send_transaction_view_button = Button(self.driver,
xpath="//androidx.appcompat.widget.LinearLayoutCompat")
self.hide_account_button = Button(self.driver, accessibility_id="hide-account-button")
# collectibles def confirm_transaction(self):
self.collectibles_button = Button(self.driver, translation_id="wallet-collectibles") self.confirm_button.click_until_presence_of_element(self.slide_button_track)
self.nft_asset_button = Button(self.driver, accessibility_id="nft-asset") self.slide_and_confirm_with_password()
self.set_collectible_as_profile_photo_button = Button(self.driver, accessibility_id="set-nft-as-pfp") self.done_button.click()
self.view_collectible_on_opensea_button = Button(self.driver, translation_id="view-on-opensea")
# individual account settings def send_asset(self, address: str, asset_name: str, amount: float):
self.account_settings_button = Button(self.driver, translation_id="account-settings") self.send_button.click()
self.apply_settings_button = Button(self.driver, translation_id="apply") self.address_text_input.send_keys(address)
self.password_delete_account_input = EditBox(self.driver, self.continue_button.click_until_presence_of_element(self.collectibles_tab)
xpath='//*[@text="Password"]/following-sibling::*/android.widget.EditText') self.select_asset(asset_name).click()
self.delete_account_confirm_button = Button(self.driver, accessibility_id="delete-account-confirm") self.amount_input.send_keys('{:f}'.format(amount).rstrip('0'))
self.confirm_transaction()
def wait_balance_is_equal_expected_amount(self, asset='ETH', expected_balance=0.1, wait_time=300, main_screen=True): def send_asset_from_drawer(self, address: str, asset_name: str, amount: float):
counter = 0 asset_element = self.get_asset(asset_name)
while True: asset_element.long_press_element()
if counter >= wait_time: self.send_button.wait_for_elements()
self.driver.fail('**Balance is not changed during %s seconds!**' % wait_time) self.send_button.find_elements()[0].click()
elif self.get_asset_amount_by_name(asset) != expected_balance: self.address_text_input.send_keys(address)
counter += 10 self.continue_button.click_until_presence_of_element(self.confirm_button)
time.sleep(10) self.amount_input.send_keys('{:f}'.format(amount).rstrip('0'))
self.swipe_down() self.confirm_transaction()
self.driver.info('Waiting %s seconds for %s balance update to be equal to %s' % (
counter, asset, expected_balance))
else:
self.driver.info('Balance for %s is equal to %s' % (asset, expected_balance))
if main_screen:
if not self.accounts_status_account.is_element_displayed():
self.accounts_status_account.scroll_to_element(direction='up')
return
def wait_balance_is_changed(self, asset='ETH', initial_balance=0, wait_time=180, scan_tokens=False, navigate_to_home=True): def add_regular_account(self, account_name: str):
self.driver.info('Waiting %ss for %s updated balance' % (wait_time, asset))
counter = 0
while True:
if counter >= wait_time:
self.driver.fail(
'Balance %s %s is not changed during %s seconds!' % (asset, initial_balance, wait_time))
elif self.asset_by_name(asset).is_element_displayed() and self.get_asset_amount_by_name(
asset) == initial_balance:
if scan_tokens:
self.scan_tokens()
if (counter / 60).is_integer():
self.pull_to_refresh()
counter += 20
counter += 10
time.sleep(10)
self.driver.info('Waiting %ss for %s updated balance' % (counter, asset))
elif not self.asset_by_name(asset).is_element_displayed(10):
if scan_tokens:
self.scan_tokens()
self.swipe_up()
counter += 10
time.sleep(10)
self.driver.info('Waiting %s seconds for %s to display asset' % (counter, asset))
else:
self.driver.info('Initial "%s" is not equal expected balance "%s", it is updated!' % (initial_balance,
self.get_asset_amount_by_name(asset)))
if navigate_to_home:
self.wallet_button.double_click()
self.element_by_translation_id("wallet-total-value").scroll_to_element(direction='up')
return self
def get_sign_in_phrase(self):
return ' '.join([element.text for element in self.sign_in_phrase.find_elements()])
def set_up_wallet_when_sending_tx(self):
self.driver.info("Setting up wallet")
phrase = self.sign_in_phrase.text
self.ok_got_it_button.click()
return phrase
def get_wallet_address(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting wallet address for '%s'" % account_name)
self.wallet_account_by_name(account_name).click()
self.receive_transaction_button.click_until_presence_of_element(self.qr_code_image)
address = self.address_text.text
self.close_share_popup()
return address
def wallet_account_by_name(self, account_name):
self.driver.info("Getting '%s' wallet account" % account_name)
return AccountElementButton(self.driver, account_name)
def get_asset_amount_by_name(self, asset: str):
self.driver.info("Getting %s amount" % asset)
asset_value = SilentButton(self.driver, xpath="//android.view.ViewGroup[@content-desc=':%s-asset-value']"
"//android.widget.TextView[1]" % asset)
for _ in range(2):
if not asset_value.is_element_displayed():
self.element = asset_value.scroll_to_element()
try:
value = float(asset_value.text.split()[0])
self.driver.info("%s value is %s" % (asset, value))
return value
except ValueError:
self.driver.info("No value for %s" % asset)
return 0.0
def asset_by_name(self, asset_name):
self.driver.info("Selecting %s asset" % asset_name)
return SilentButton(self.driver, xpath="//*[contains(@text,'%s')]" % asset_name)
def asset_checkbox_by_name(self, asset_name):
self.driver.info("Selecting %s asset checkbox by name" % asset_name)
return AssetCheckBox(self.driver, asset_name)
def get_account_options_by_name(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting '%s'account options" % account_name)
return SilentButton(self.driver, xpath="(//*[@text='%s']/../..//*[@content-desc='icon'])[2]" % account_name)
def get_account_options_from_main_screen(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting '%s'account options from main wallet screen" % account_name)
return SilentButton(self.driver,
xpath="//*[@content-desc='accountcard%s']//*[@content-desc='icon']" % account_name)
def hidden_account_by_name_button(self, account_name=''):
return SilentButton(self.driver,
xpath="//*[@text='%s']/following-sibling::*[@content-desc='hide-icon']" % account_name)
def show_account_by_name_button(self, account_name=''):
return SilentButton(self.driver,
xpath="//*[@text='%s']/following-sibling::*[@content-desc='show-icon']" % account_name)
def select_asset(self, *args):
self.driver.info("Selecting asset(s)")
self.multiaccount_more_options.click()
self.manage_assets_button.click()
for asset in args:
self.element_by_text(asset).scroll_to_element()
self.element_by_text(asset).scroll_and_click()
self.cross_icon.click()
def scan_tokens(self, *args):
self.driver.info("Scanning tokens")
self.multiaccount_more_options.click()
self.scan_tokens_button.click()
counter = 0
if args:
for asset in args:
while True:
if counter >= 20:
self.driver.fail('Balance of %s is not changed during 20 seconds!' % asset)
elif self.get_asset_amount_by_name(asset) == 0.0:
self.multiaccount_more_options.click()
self.scan_tokens_button.click()
self.driver.info('Trying to scan for tokens one more time and waiting %s seconds for %s '
'to update' % (counter, asset))
time.sleep(5)
counter += 5
else:
self.driver.info('Balance of %s is updated!' % asset)
return self
def send_transaction(self, **kwargs):
self.driver.info("## Sending transaction", device=False)
send_tx = self.send_transaction_from_main_screen.click() if kwargs.get('from_main_wallet',
True) else self.send_transaction_button.click()
send_tx.select_asset_button.click()
asset_name = kwargs.get('asset_name', 'ETH').upper()
asset_button = send_tx.asset_by_name(asset_name)
send_tx.select_asset_button.click_until_presence_of_element(
send_tx.eth_asset_in_select_asset_bottom_sheet_button)
asset_button.click()
send_tx.amount_edit_box.click()
transaction_amount = str(kwargs.get('amount', send_tx.get_unique_amount()))
send_tx.amount_edit_box.send_keys(transaction_amount)
if kwargs.get('account_name'):
send_tx.chose_recipient_button.click()
send_tx.accounts_button.click()
send_tx.element_by_text(kwargs.get('account_name')).click()
else:
send_tx.set_recipient_address(kwargs.get('recipient'))
if kwargs.get('sign_transaction', True):
send_tx.sign_transaction_button.click()
if self.sign_in_phrase.is_element_displayed():
self.set_up_wallet_when_sending_tx()
send_tx.sign_transaction(keycard=kwargs.get('keycard', False),
sender_password=kwargs.get('sender_password', common_password))
return send_tx
def find_transaction_in_history(self, amount, asset='ETH', account_name=None, return_hash=False):
if account_name is None:
account_name = self.status_account_name
self.driver.info("Finding '%s %s' transaction for '%s'" % (amount, asset, account_name))
if not self.transaction_history_button.is_element_displayed():
self.get_account_by_name(account_name).click()
self.transaction_history_button.wait_for_element()
transactions_view = self.transaction_history_button.click()
transaction_element = transactions_view.transactions_table.find_transaction(amount=amount, asset=asset)
result = transaction_element
if return_hash:
transaction_element.click()
from views.transactions_view import TransactionTable
result = TransactionTable.TransactionElement.TransactionDetailsView(self.driver).get_transaction_hash()
return result
def set_currency(self, desired_currency='EUR'):
self.driver.info("Setting '%s' currency" % desired_currency)
self.multiaccount_more_options.click_until_presence_of_element(self.set_currency_button)
self.set_currency_button.click()
desired_currency = self.element_by_text_part(desired_currency)
desired_currency.scroll_to_element()
desired_currency.click()
def get_account_by_name(self, account_name: str):
self.driver.info("Getting account: '%s'" % account_name)
return AccountElementButton(self.driver, account_name)
def add_account(self, account_name: str, password: str = common_password, keycard=False):
self.driver.info("## Add account: '%s'" % account_name, device=False)
self.add_account_button.click() self.add_account_button.click()
self.generate_an_account_button.click() self.create_account_button.click()
self.account_name_input.send_keys(account_name) SignInView(self.driver).profile_title_input.send_keys(account_name)
if keycard: self.slide_and_confirm_with_password()
from views.keycard_view import KeycardView
keycard_view = KeycardView(self.driver)
self.add_account_generate_account_button.click()
keycard_view.enter_default_pin()
else:
self.enter_your_password_input.send_keys(password)
self.add_account_generate_account_button.click_until_presence_of_element(self.accounts_status_account)
self.driver.info("## Account is added!", device=False)
def get_collectibles_amount(self, collectibles='CryptoKitties'): def add_watch_only_account(self, address: str, account_name: str):
self.driver.info("Getting '%s' Collectibles amount" % collectibles) self.add_account_button.click()
return Text(self.driver, xpath="//*[@text='%s']//following-sibling::android.widget.TextView" % collectibles) self.add_account_to_watch.click()
self.address_to_watch_input.send_keys(address)
self.account_has_activity_label.wait_for_visibility_of_element()
self.add_account_continue_button.click()
SignInView(self.driver).profile_title_input.send_keys(account_name)
self.add_watched_address_button.click()
def remove_account(self, account_name: str, watch_only: bool = False):
self.get_account_element(account_name=account_name).click()
self.account_emoji_button.click()
self.remove_account_button.click()
if not watch_only:
self.derivation_path_note_checkbox.click()
self.confirm_button.click()

View File

@ -0,0 +1,378 @@
import time
from tests import common_password
from views.base_element import Button, Text, EditBox, SilentButton, CheckBox
from views.base_view import BaseView
class TransactionHistoryButton(Button):
def __init__(self, driver):
super().__init__(driver, accessibility_id="History-item-button")
def navigate(self):
from views.transactions_view import TransactionsView
return TransactionsView(self.driver)
class AssetCheckBox(CheckBox):
def __init__(self, driver, asset_name):
super().__init__(driver, xpath="//*[@text='%s']" % asset_name)
def enable(self):
self.scroll_to_element(12)
super().enable()
class BackupRecoveryPhrase(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="wallet-backup-recovery-title")
def navigate(self):
from views.profile_view import ProfileView
return ProfileView(self.driver)
class AccountElementButton(SilentButton):
def __init__(self, driver, account_name):
super().__init__(driver, xpath="//*[@content-desc='accountcard%s']" % account_name)
def color_matches(self, expected_color_image_name: str):
amount_text = Text(self.driver, xpath="%s//*[@content-desc='account-total-value']" % self.locator)
amount_text.wait_for_element_text('...', 60)
return not amount_text.is_element_differs_from_template(expected_color_image_name)
class SendTransactionButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="wallet-send")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class SendTransactionFromMainButton(Button):
def __init__(self, driver):
super().__init__(driver, accessibility_id="send-transaction-button")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class ReceiveTransactionButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="receive")
def navigate(self):
from views.send_transaction_view import SendTransactionView
return SendTransactionView(self.driver)
class AddCustomTokenButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="add-custom-token")
def navigate(self):
from views.add_custom_token_view import AddCustomTokenView
return AddCustomTokenView(self.driver)
class AccountColorButton(Button):
def __init__(self, driver):
super().__init__(driver, translation_id="account-color", suffix="/following-sibling::android.view.ViewGroup[1]")
def select_color_by_position(self, position: int):
self.click()
self.driver.find_element_by_xpath(
"((//android.widget.ScrollView)[1]/*/*)[%s]" % str(position + 1)).click()
class WalletView(BaseView):
def __init__(self, driver):
super().__init__(driver)
self.send_transaction_button = SendTransactionButton(self.driver)
self.send_transaction_from_main_screen = SendTransactionFromMainButton(self.driver)
self.transaction_history_button = TransactionHistoryButton(self.driver)
self.usd_total_value = Text(self.driver, accessibility_id="total-amount-value-text")
self.receive_transaction_button = ReceiveTransactionButton(self.driver)
self.options_button = Button(self.driver, accessibility_id="options-menu-button")
self.manage_assets_button = Button(self.driver, accessibility_id="wallet-manage-assets")
self.manage_accounts_button = Button(self.driver, accessibility_id="wallet-manage-accounts")
self.scan_tokens_button = Button(self.driver, accessibility_id="wallet-scan-token")
self.all_assets_full_names = Text(self.driver,
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[1]")
self.all_assets_symbols = Button(self.driver,
xpath="//*[@content-desc='checkbox-off']/../android.widget.TextView[2]")
self.currency_item_text = Text(self.driver, xpath="//*[@content-desc='currency-item']//android.widget.TextView")
self.address_text = Text(self.driver, accessibility_id="address-text")
self.remind_me_later_button = Button(self.driver, translation_id="remind-me-later")
self.total_amount_text = Text(self.driver, accessibility_id="total-amount-value-text")
self.currency_text = Text(self.driver, accessibility_id="total-amount-currency-text")
self.backup_recovery_phrase = BackupRecoveryPhrase(self.driver)
self.backup_recovery_phrase_warning_text = Text(self.driver,
accessibility_id="back-up-your-seed-phrase-warning")
self.add_custom_token_button = AddCustomTokenButton(self.driver)
# elements for multiaccount
self.multiaccount_more_options = Button(self.driver, accessibility_id="accounts-more-options")
self.accounts_status_account = AccountElementButton(self.driver, account_name=self.status_account_name)
self.set_currency_button = Button(self.driver, translation_id="set-currency")
self.add_account_button = Button(self.driver, accessibility_id="add-new-account")
self.generate_an_account_button = Button(self.driver, accessibility_id="add-account-sheet-generate")
self.add_watch_only_address_button = Button(self.driver, accessibility_id="add-account-sheet-watch")
self.enter_a_seed_phrase_button = Button(self.driver, accessibility_id="add-account-sheet-seed")
self.enter_a_private_key_button = Button(self.driver, accessibility_id="add-account-sheet-private-key")
self.enter_address_input = EditBox(self.driver, accessibility_id="add-account-enter-watch-address")
self.enter_seed_phrase_input = EditBox(self.driver, accessibility_id="add-account-enter-seed")
self.enter_a_private_key_input = EditBox(self.driver, accessibility_id="add-account-enter-private-key")
self.delete_account_button = Button(self.driver, translation_id="delete-account")
self.enter_your_password_input = EditBox(self.driver, accessibility_id="add-account-enter-password")
self.account_name_input = EditBox(self.driver, accessibility_id="enter-account-name")
self.account_color_button = AccountColorButton(self.driver)
self.add_account_generate_account_button = Button(self.driver,
accessibility_id="add-account-add-account-button")
self.status_account_total_usd_value = Text(self.driver, accessibility_id="account-total-value")
self.scan_qr_button = Button(self.driver, accessibility_id="accounts-qr-code")
self.close_send_transaction_view_button = Button(self.driver,
xpath="//androidx.appcompat.widget.LinearLayoutCompat")
self.hide_account_button = Button(self.driver, accessibility_id="hide-account-button")
# collectibles
self.collectibles_button = Button(self.driver, translation_id="wallet-collectibles")
self.nft_asset_button = Button(self.driver, accessibility_id="nft-asset")
self.set_collectible_as_profile_photo_button = Button(self.driver, accessibility_id="set-nft-as-pfp")
self.view_collectible_on_opensea_button = Button(self.driver, translation_id="view-on-opensea")
# individual account settings
self.account_settings_button = Button(self.driver, translation_id="account-settings")
self.apply_settings_button = Button(self.driver, translation_id="apply")
self.password_delete_account_input = EditBox(self.driver,
xpath='//*[@text="Password"]/following-sibling::*/android.widget.EditText')
self.delete_account_confirm_button = Button(self.driver, accessibility_id="delete-account-confirm")
def wait_balance_is_equal_expected_amount(self, asset='ETH', expected_balance=0.1, wait_time=300, main_screen=True):
counter = 0
while True:
if counter >= wait_time:
self.driver.fail('**Balance is not changed during %s seconds!**' % wait_time)
elif self.get_asset_amount_by_name(asset) != expected_balance:
counter += 10
time.sleep(10)
self.swipe_down()
self.driver.info('Waiting %s seconds for %s balance update to be equal to %s' % (
counter, asset, expected_balance))
else:
self.driver.info('Balance for %s is equal to %s' % (asset, expected_balance))
if main_screen:
if not self.accounts_status_account.is_element_displayed():
self.accounts_status_account.scroll_to_element(direction='up')
return
def wait_balance_is_changed(self, asset='ETH', initial_balance=0, wait_time=180, scan_tokens=False, navigate_to_home=True):
self.driver.info('Waiting %ss for %s updated balance' % (wait_time, asset))
counter = 0
while True:
if counter >= wait_time:
self.driver.fail(
'Balance %s %s is not changed during %s seconds!' % (asset, initial_balance, wait_time))
elif self.asset_by_name(asset).is_element_displayed() and self.get_asset_amount_by_name(
asset) == initial_balance:
if scan_tokens:
self.scan_tokens()
if (counter / 60).is_integer():
self.pull_to_refresh()
counter += 20
counter += 10
time.sleep(10)
self.driver.info('Waiting %ss for %s updated balance' % (counter, asset))
elif not self.asset_by_name(asset).is_element_displayed(10):
if scan_tokens:
self.scan_tokens()
self.swipe_up()
counter += 10
time.sleep(10)
self.driver.info('Waiting %s seconds for %s to display asset' % (counter, asset))
else:
self.driver.info('Initial "%s" is not equal expected balance "%s", it is updated!' % (initial_balance,
self.get_asset_amount_by_name(asset)))
if navigate_to_home:
self.wallet_button.double_click()
self.element_by_translation_id("wallet-total-value").scroll_to_element(direction='up')
return self
def get_sign_in_phrase(self):
return ' '.join([element.text for element in self.sign_in_phrase.find_elements()])
def set_up_wallet_when_sending_tx(self):
self.driver.info("Setting up wallet")
phrase = self.sign_in_phrase.text
self.ok_got_it_button.click()
return phrase
def get_wallet_address(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting wallet address for '%s'" % account_name)
self.wallet_account_by_name(account_name).click()
self.receive_transaction_button.click_until_presence_of_element(self.qr_code_image)
address = self.address_text.text
self.close_share_popup()
return address
def wallet_account_by_name(self, account_name):
self.driver.info("Getting '%s' wallet account" % account_name)
return AccountElementButton(self.driver, account_name)
def get_asset_amount_by_name(self, asset: str):
self.driver.info("Getting %s amount" % asset)
asset_value = SilentButton(self.driver, xpath="//android.view.ViewGroup[@content-desc=':%s-asset-value']"
"//android.widget.TextView[1]" % asset)
for _ in range(2):
if not asset_value.is_element_displayed():
self.element = asset_value.scroll_to_element()
try:
value = float(asset_value.text.split()[0])
self.driver.info("%s value is %s" % (asset, value))
return value
except ValueError:
self.driver.info("No value for %s" % asset)
return 0.0
def asset_by_name(self, asset_name):
self.driver.info("Selecting %s asset" % asset_name)
return SilentButton(self.driver, xpath="//*[contains(@text,'%s')]" % asset_name)
def asset_checkbox_by_name(self, asset_name):
self.driver.info("Selecting %s asset checkbox by name" % asset_name)
return AssetCheckBox(self.driver, asset_name)
def get_account_options_by_name(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting '%s'account options" % account_name)
return SilentButton(self.driver, xpath="(//*[@text='%s']/../..//*[@content-desc='icon'])[2]" % account_name)
def get_account_options_from_main_screen(self, account_name=''):
account_name = self.status_account_name if not account_name else account_name
self.driver.info("Getting '%s'account options from main wallet screen" % account_name)
return SilentButton(self.driver,
xpath="//*[@content-desc='accountcard%s']//*[@content-desc='icon']" % account_name)
def hidden_account_by_name_button(self, account_name=''):
return SilentButton(self.driver,
xpath="//*[@text='%s']/following-sibling::*[@content-desc='hide-icon']" % account_name)
def show_account_by_name_button(self, account_name=''):
return SilentButton(self.driver,
xpath="//*[@text='%s']/following-sibling::*[@content-desc='show-icon']" % account_name)
def select_asset(self, *args):
self.driver.info("Selecting asset(s)")
self.multiaccount_more_options.click()
self.manage_assets_button.click()
for asset in args:
self.element_by_text(asset).scroll_to_element()
self.element_by_text(asset).scroll_and_click()
self.cross_icon.click()
def scan_tokens(self, *args):
self.driver.info("Scanning tokens")
self.multiaccount_more_options.click()
self.scan_tokens_button.click()
counter = 0
if args:
for asset in args:
while True:
if counter >= 20:
self.driver.fail('Balance of %s is not changed during 20 seconds!' % asset)
elif self.get_asset_amount_by_name(asset) == 0.0:
self.multiaccount_more_options.click()
self.scan_tokens_button.click()
self.driver.info('Trying to scan for tokens one more time and waiting %s seconds for %s '
'to update' % (counter, asset))
time.sleep(5)
counter += 5
else:
self.driver.info('Balance of %s is updated!' % asset)
return self
def send_transaction(self, **kwargs):
self.driver.info("## Sending transaction", device=False)
send_tx = self.send_transaction_from_main_screen.click() if kwargs.get('from_main_wallet',
True) else self.send_transaction_button.click()
send_tx.select_asset_button.click()
asset_name = kwargs.get('asset_name', 'ETH').upper()
asset_button = send_tx.asset_by_name(asset_name)
send_tx.select_asset_button.click_until_presence_of_element(
send_tx.eth_asset_in_select_asset_bottom_sheet_button)
asset_button.click()
send_tx.amount_edit_box.click()
transaction_amount = str(kwargs.get('amount', send_tx.get_unique_amount()))
send_tx.amount_edit_box.send_keys(transaction_amount)
if kwargs.get('account_name'):
send_tx.chose_recipient_button.click()
send_tx.accounts_button.click()
send_tx.element_by_text(kwargs.get('account_name')).click()
else:
send_tx.set_recipient_address(kwargs.get('recipient'))
if kwargs.get('sign_transaction', True):
send_tx.sign_transaction_button.click()
if self.sign_in_phrase.is_element_displayed():
self.set_up_wallet_when_sending_tx()
send_tx.sign_transaction(keycard=kwargs.get('keycard', False),
sender_password=kwargs.get('sender_password', common_password))
return send_tx
def find_transaction_in_history(self, amount, asset='ETH', account_name=None, return_hash=False):
if account_name is None:
account_name = self.status_account_name
self.driver.info("Finding '%s %s' transaction for '%s'" % (amount, asset, account_name))
if not self.transaction_history_button.is_element_displayed():
self.get_account_by_name(account_name).click()
self.transaction_history_button.wait_for_element()
transactions_view = self.transaction_history_button.click()
transaction_element = transactions_view.transactions_table.find_transaction(amount=amount, asset=asset)
result = transaction_element
if return_hash:
transaction_element.click()
from views.transactions_view import TransactionTable
result = TransactionTable.TransactionElement.TransactionDetailsView(self.driver).get_transaction_hash()
return result
def set_currency(self, desired_currency='EUR'):
self.driver.info("Setting '%s' currency" % desired_currency)
self.multiaccount_more_options.click_until_presence_of_element(self.set_currency_button)
self.set_currency_button.click()
desired_currency = self.element_by_text_part(desired_currency)
desired_currency.scroll_to_element()
desired_currency.click()
def get_account_by_name(self, account_name: str):
self.driver.info("Getting account: '%s'" % account_name)
return AccountElementButton(self.driver, account_name)
def add_account(self, account_name: str, password: str = common_password, keycard=False):
self.driver.info("## Add account: '%s'" % account_name, device=False)
self.add_account_button.click()
self.generate_an_account_button.click()
self.account_name_input.send_keys(account_name)
if keycard:
from views.keycard_view import KeycardView
keycard_view = KeycardView(self.driver)
self.add_account_generate_account_button.click()
keycard_view.enter_default_pin()
else:
self.enter_your_password_input.send_keys(password)
self.add_account_generate_account_button.click_until_presence_of_element(self.accounts_status_account)
self.driver.info("## Account is added!", device=False)
def get_collectibles_amount(self, collectibles='CryptoKitties'):
self.driver.info("Getting '%s' Collectibles amount" % collectibles)
return Text(self.driver, xpath="//*[@text='%s']//following-sibling::android.widget.TextView" % collectibles)