WIP, report automated test results to GitHub as comment under appropriate PR

Signed-off-by: Eric Dvorsak <eric@dvorsak.fr>
This commit is contained in:
Anton Danchenko 2018-01-26 13:07:09 +02:00 committed by Eric Dvorsak
parent 8582be2e69
commit f3a1248b13
No known key found for this signature in database
GPG Key ID: 932AC1CE5F05DE0C
23 changed files with 563 additions and 395 deletions

10
Jenkinsfile vendored
View File

@ -91,6 +91,16 @@ node ('macos1') {
'\niOS: ' + ipaUrl
}
stage('Run e2e tests') {
if (env.CHANGE_ID != null){
withCredentials([string(credentialsId: 'SAUCE_ACCESS_KEY', variable: 'key'), string(credentialsId: 'SAUCE_USERNAME', variable: 'username')]){
def apk_name = env.CHANGE_ID + '.apk'
sh('curl -u ' + username+ ':' + key + ' -X POST -H "Content-Type: application/octet-stream" https://saucelabs.com/rest/v1/storage/' + username + '/' + apk_name + '?overwrite=true --data-binary @android/app/build/outputs/apk/release/app-release.apk')
build job: 'end-to-end-tests/status-app-end-to-end-tests', parameters: [string(name: 'apk', value: '--apk=' + apk_name), string(name: 'pr_id', value: env.CHANGE_ID)], wait: false
}
}
}
} catch (e) {
slackSend color: 'bad', message: BRANCH_NAME + ' failed to build. ' + env.BUILD_URL
throw e

View File

@ -1,3 +1,3 @@
[pytest]
norecursedirs = .git views
addopts = -s --junitxml=result.xml --tb=short
addopts = -s -v --junitxml=result.xml --tb=line

View File

@ -21,3 +21,5 @@ selenium==3.8.1
six==1.10.0
urllib3==1.22
yarl==0.12.0
zbarlight=1.2
eth-keys==0.1.0b4

View File

@ -1,16 +1,26 @@
import asyncio
import logging
from datetime import datetime
@asyncio.coroutine
def start_threads(amount, func, *args):
features = dict()
def start_threads(quantity: int, func: type, returns: dict, *args):
loop = asyncio.get_event_loop()
for i in range(amount):
features['feature_' + str(i)] = loop.run_in_executor(None, func, *args)
for k in features:
features[k] = yield from features[k]
return (features[k] for k in features)
for i in range(quantity):
returns[i] = loop.run_in_executor(None, func, *args)
for k in returns:
returns[k] = yield from returns[k]
return returns
def get_current_time():
return datetime.now().strftime('%-m%-d%-H%-M%-S')
def info(text: str):
if "Base" not in text:
logging.info(text)
test_data.test_info[test_data.test_name]['steps'] += text + '\n'
class TestData(object):
@ -18,37 +28,40 @@ class TestData(object):
def __init__(self):
self.test_name = None
self.apk_name = None
self.test_info = dict()
test_data = TestData()
basic_user = {'password': "newuniquepassword12",
'passphrase': "tree weekend ceiling awkward universe pyramid glimpse raven pair lounge grant grief",
'username': "Splendid Useless Racerunner"}
basic_user = dict()
basic_user['password'] = "newuniquepassword12"
basic_user['passphrase'] = "tree weekend ceiling awkward universe pyramid glimpse raven pair lounge grant grief"
basic_user['username'] = "Splendid Useless Racerunner"
basic_user['public_key'] = "0x0448243ea6adfd2f825f083a02a1fea11e323a3ba32c9dc9992d3d465e932964" \
"38792f11380e14c6700f598e89bafaddd2579823f4273358f9f66828fcac7dd465"
transaction_users = {
'A_USER': {'password': "qwerty",
'passphrase': "pet letter very ozone shop humor "
"shuffle bounce convince soda hint brave",
'public_key': '0x040e016b940e067997be8d91298d893ff2bc3580504b4ccb155ea03d183b85f1'
'8e771a763d99f60fec70edf637eb6bad9f96d3e8a544168d3ad144f83b4cf7625c',
'address': '67a50ef1d26de6d65dbfbb88172ac1e7017e766d',
'username': 'Evergreen Handsome Cottontail'},
transaction_users = dict()
transaction_users['A_USER'] = dict()
transaction_users['A_USER']['password'] = "qwerty"
transaction_users['A_USER']['passphrase'] = "pet letter very ozone shop humor " \
"shuffle bounce convince soda hint brave"
'B_USER': {'password': "qwerty",
'passphrase': "resemble soap taxi meat reason "
"inflict dilemma calm warrior key gloom again",
'public_key': '0x0406b17e5cdfadb2a05e84508b1a2916def6395e6295f57e92b85f915d40bca3'
'f4a7e4c6d6b25afa840dd042fac83d3f856181d553f34f1c2b12878e774adde099',
'address': '3d672407a7e1250bbff85b7cfdb456f5015164db',
'username': 'Brief Organic Xenops'
},
}
transaction_users['A_USER']['username'] = "Evergreen Handsome Cottontail"
transaction_users['A_USER']['address'] = "67a50ef1d26de6d65dbfbb88172ac1e7017e766d"
transaction_users['A_USER']['public_key'] = "0x040e016b940e067997be8d91298d893ff2bc3580504b4ccb155ea03d183b85f1" \
"8e771a763d99f60fec70edf637eb6bad9f96d3e8a544168d3ad144f83b4cf7625c"
transaction_users['B_USER'] = dict()
transaction_users['B_USER']['password'] = "qwerty"
transaction_users['B_USER']['passphrase'] = "resemble soap taxi meat reason " \
"inflict dilemma calm warrior key gloom again"
transaction_users['B_USER']['username'] = "Brief Organic Xenops"
transaction_users['B_USER']['address'] = "3d672407a7e1250bbff85b7cfdb456f5015164db"
transaction_users['B_USER']['public_key'] = "0x0406b17e5cdfadb2a05e84508b1a2916def6395e6295f57e92b85f915d40bca3" \
"f4a7e4c6d6b25afa840dd042fac83d3f856181d553f34f1c2b12878e774adde099"
transaction_users_wallet = dict()
transaction_users_wallet['A_USER'] = dict()
transaction_users_wallet['A_USER']['password'] = "new_unique_password"
transaction_users_wallet['A_USER']['passphrase'] = "kiss catch paper awesome ecology surface " \
@ -67,6 +80,3 @@ transaction_users_wallet['B_USER']['address'] = "5261ceba31e3a7204b498b2dd20220a
transaction_users_wallet['B_USER']['public_key'] = "0x04cd70746f3df6cae7b45c32c211bd7e9e95ed5a1ec470db8f3b1f244eed182" \
"1d4a2053d7671802c5f7ce5b81f5fc2016a8109e1bc83f151ceff21f08c0cdcc6e4"
def get_current_time():
return datetime.now().strftime('%-m%-d%-H%-M%-S')

View File

@ -1,7 +1,7 @@
import logging
import pytest
import requests
import time
from tests import info
def get_transactions(address: str) -> dict:
@ -27,7 +27,7 @@ def find_transaction_on_ropsten(address: str, transaction_hash: str):
transactions = get_transactions(address=address)
for transaction in transactions:
if transaction['hash'] == transaction_hash:
logging.info('Transaction is found in Ropsten network')
info('Transaction is found in Ropsten network')
return
pytest.fail('Transaction is not found in Ropsten network')
@ -40,9 +40,9 @@ def verify_balance_is_updated(initial_balance, recipient_address, wait_time=240)
elif initial_balance == get_balance(recipient_address):
counter += 10
time.sleep(10)
logging.info('Waiting %s seconds for funds' % counter)
info('Waiting %s seconds for funds' % counter)
else:
logging.info('Transaction is received')
info('Transaction is received')
return
@ -57,9 +57,9 @@ def get_donate(address, wait_time=300):
elif get_balance(address) == initial_balance:
counter += 10
time.sleep(10)
logging.info('Waiting %s seconds for donation' % counter)
info('Waiting %s seconds for donation' % counter)
else:
logging.info('Got %s for %s' % (response["amount_eth"], address))
info('Got %s for %s' % (response["amount_eth"], address))
return

View File

@ -1,29 +1,27 @@
import pytest
import sys
from tests import *
from os import environ
from appium import webdriver
from abc import ABCMeta, \
abstractmethod
import hmac
import re
import subprocess
from hashlib import md5
import asyncio
from selenium.common.exceptions import WebDriverException
from tests import test_data, start_threads
from os import environ
from appium import webdriver
from abc import ABCMeta, abstractmethod
class AbstractTestCase:
__metaclass__ = ABCMeta
@property
def sauce_access_key(self):
return environ.get('SAUCE_ACCESS_KEY')
@property
def sauce_username(self):
return environ.get('SAUCE_USERNAME')
@property
def sauce_access_key(self):
return environ.get('SAUCE_ACCESS_KEY')
@property
def executor_sauce_lab(self):
return 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (self.sauce_username, self.sauce_access_key)
@ -32,16 +30,10 @@ class AbstractTestCase:
def executor_local(self):
return 'http://localhost:4723/wd/hub'
def get_public_url(self, driver):
token = hmac.new(bytes(self.sauce_username + ":" + self.sauce_access_key, 'latin-1'),
bytes(driver.session_id, 'latin-1'), md5).hexdigest()
return "https://saucelabs.com/jobs/%s?auth=%s" % (driver.session_id, token)
def print_sauce_lab_info(self, driver):
sys.stdout = sys.stderr
print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id,
pytest.config.getoption('build')))
print(self.get_public_url(driver))
def add_local_devices_to_capabilities(self):
updated_capabilities = list()
@ -79,7 +71,7 @@ class AbstractTestCase:
desired_caps['appiumVersion'] = '1.7.1'
desired_caps['platformVersion'] = '6.0'
desired_caps['newCommandTimeout'] = 600
desired_caps['fullReset'] = True
desired_caps['fullReset'] = False
return desired_caps
@abstractmethod
@ -98,20 +90,51 @@ class AbstractTestCase:
def implicitly_wait(self):
return 10
def update_test_info_dict(self):
test_data.test_info[test_data.test_name] = dict()
test_data.test_info[test_data.test_name]['jobs'] = list()
test_data.test_info[test_data.test_name]['steps'] = str()
class SingleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
self.update_test_info_dict()
capabilities = {'local': {'executor': self.executor_local,
'capabilities': self.capabilities_local},
'sauce': {'executor': self.executor_sauce_lab,
'capabilities': self.capabilities_sauce_lab}}
self.driver = webdriver.Remote(capabilities[self.environment]['executor'],
capabilities[self.environment]['capabilities'])
self.driver.implicitly_wait(self.implicitly_wait)
test_data.test_info[test_data.test_name]['jobs'].append(self.driver.session_id)
def teardown_method(self, method):
if self.environment == 'sauce':
self.print_sauce_lab_info(self.driver)
try:
self.driver.quit()
except WebDriverException:
pass
class LocalMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
self.drivers = dict()
def create_drivers(self, quantity):
capabilities = self.add_local_devices_to_capabilities()
self.driver_1 = webdriver.Remote(self.executor_local, capabilities[0])
self.driver_2 = webdriver.Remote(self.executor_local, capabilities[1])
for driver in self.driver_1, self.driver_2:
driver.implicitly_wait(self.implicitly_wait)
for driver in range(quantity):
self.drivers[driver] = webdriver.Remote(self.executor_local, capabilities[driver])
self.drivers[driver].implicitly_wait(self.implicitly_wait)
def teardown_method(self, method):
for driver in self.driver_1, self.driver_2:
for driver in self.drivers:
try:
driver.quit()
self.drivers[driver].quit()
except WebDriverException:
pass
@ -123,19 +146,23 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
cls.loop = asyncio.get_event_loop()
def setup_method(self, method):
self.driver_1, \
self.driver_2 = self.loop.run_until_complete(start_threads(2,
webdriver.Remote,
self.executor_sauce_lab,
self.capabilities_sauce_lab))
for driver in self.driver_1, self.driver_2:
driver.implicitly_wait(self.implicitly_wait)
self.update_test_info_dict()
self.drivers = dict()
def create_drivers(self, quantity=2):
self.drivers = self.loop.run_until_complete(start_threads(quantity, webdriver.Remote,
self.drivers,
self.executor_sauce_lab,
self.capabilities_sauce_lab))
for driver in range(quantity):
self.drivers[driver].implicitly_wait(self.implicitly_wait)
test_data.test_info[test_data.test_name]['jobs'].append(self.drivers[driver].session_id)
def teardown_method(self, method):
for driver in self.driver_1, self.driver_2:
self.print_sauce_lab_info(driver)
for driver in self.drivers:
self.print_sauce_lab_info(self.drivers[driver])
try:
driver.quit()
self.drivers[driver].quit()
except WebDriverException:
pass
@ -144,28 +171,6 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
cls.loop.close()
class SingleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
capabilities = {'local': {'executor': self.executor_local,
'capabilities': self.capabilities_local},
'sauce': {'executor': self.executor_sauce_lab,
'capabilities': self.capabilities_sauce_lab}}
self.driver = webdriver.Remote(capabilities[self.environment]['executor'],
capabilities[self.environment]['capabilities'])
self.driver.implicitly_wait(self.implicitly_wait)
def teardown_method(self, method):
if self.environment == 'sauce':
self.print_sauce_lab_info(self.driver)
try:
self.driver.quit()
except WebDriverException:
pass
environments = {'local': LocalMultipleDeviceTestCase,
'sauce': SauceMultipleDeviceTestCase}

View File

@ -1,15 +1,21 @@
from tests import test_data
import requests
import re
import pytest
from datetime import datetime
from os import environ
from io import BytesIO
from sauceclient import SauceClient
from hashlib import md5
import hmac
storage = 'http://artifacts.status.im:8081/artifactory/nightlies-local/'
sauce_username = environ.get('SAUCE_USERNAME')
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
github_token = environ.get('GIT_HUB_TOKEN')
sauce = SauceClient(sauce_username, sauce_access_key)
def get_latest_apk():
@ -41,6 +47,10 @@ def pytest_addoption(parser):
action='store',
default=False,
help='Display each test step in terminal as plain text: True/False')
parser.addoption('--pr_number',
action='store',
default=None,
help='Pull Request number')
def is_master(config):
@ -48,33 +58,82 @@ def is_master(config):
def is_uploaded():
stored_files = SauceClient(sauce_username, sauce_access_key).storage.get_stored_files()
stored_files = sauce.storage.get_stored_files()
for i in range(len(stored_files['files'])):
if stored_files['files'][i]['name'] == test_data.apk_name:
return True
def pytest_configure(config):
if config.getoption('log'):
import logging
logging.basicConfig(level=logging.INFO)
test_data.apk_name = ([i for i in [i for i in config.getoption('apk').split('/')
if '.apk' in i]])[0]
if is_master(config) and config.getoption('env') == 'sauce':
if config.getoption('pr_number'):
with open('github_comment.txt', 'w') as _:
pass
if not is_uploaded():
response = requests.get(config.getoption('apk'), stream=True)
response.raise_for_status()
file = BytesIO(response.content)
del response
requests.post('http://saucelabs.com/rest/v1/storage/'
+ sauce_username + '/' + test_data.apk_name + '?overwrite=true',
auth=(sauce_username, sauce_access_key),
data=file,
headers={'Content-Type': 'application/octet-stream'})
if 'http' in config.getoption('apk'):
response = requests.get(config.getoption('apk'), stream=True)
response.raise_for_status()
file = BytesIO(response.content)
del response
requests.post('http://saucelabs.com/rest/v1/storage/'
+ sauce_username + '/' + test_data.apk_name + '?overwrite=true',
auth=(sauce_username, sauce_access_key),
data=file,
headers={'Content-Type': 'application/octet-stream'})
else:
sauce.storage.upload_file(config.getoption('apk'))
def pytest_unconfigure(config):
if is_master(config) and config.getoption('pr_number'):
from github import Github
repo = Github(github_token).get_user('status-im').get_repo('status-react')
pull = repo.get_pull(int(config.getoption('pr_number')))
with open('github_comment.txt', 'r') as comment:
pull.create_issue_comment('# Automated test results: \n' + comment.read())
def get_public_url(job_id):
token = hmac.new(bytes(sauce_username + ":" + sauce_access_key, 'latin-1'),
bytes(job_id, 'latin-1'), md5).hexdigest()
return "https://saucelabs.com/jobs/%s?auth=%s" % (job_id, token)
def make_github_report(error=None):
if pytest.config.getoption('pr_number'):
title = '### %s' % test_data.test_name
outcome = '%s' % ':x:' if error else ':white_check_mark:' + ':\n'
title += outcome
steps = '\n\n <details>\n<summary>Test Steps & Error message:</summary>\n\n ```%s ```%s\n\n</details>\n' % \
(test_data.test_info[test_data.test_name]['steps'], '\n```' + error + '```' if error else '')
sessions = str()
for job_id in test_data.test_info[test_data.test_name]['jobs']:
sessions += ' - [Android Device Session](%s) \n' % get_public_url(job_id)
with open('github_comment.txt', 'a') as comment:
comment.write(title + '\n' + steps + '\n' + sessions + '---\n')
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if pytest.config.getoption('env') == 'sauce':
if report.when == 'call':
if report.passed:
for job_id in test_data.test_info[test_data.test_name]['jobs']:
sauce.jobs.update_job(job_id, name=test_data.test_name, passed=True)
make_github_report()
if report.failed:
for job_id in test_data.test_info[test_data.test_name]['jobs']:
sauce.jobs.update_job(job_id, name=test_data.test_name, passed=False)
make_github_report(error=report.longreprtext)
def pytest_runtest_setup(item):
test_data.test_name = item.name + '_' + test_data.apk_name
test_data.test_name = item.name

View File

@ -2,63 +2,28 @@ import pytest
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from tests import api_requests
from tests.base_test_case import MultipleDeviceTestCase
from tests import user_flow, transaction_users, get_current_time
from tests import transaction_users, get_current_time
from views.console_view import ConsoleView
@pytest.mark.all
class TestMultipleDevices(MultipleDeviceTestCase):
@pytest.mark.skip('Discover functionality is not available')
@pytest.mark.discover
def test_new_profile_name_and_status_on_discover(self):
device_1, device_2 = ConsoleView(self.driver_1), ConsoleView(self.driver_2)
for profile in device_1, device_2:
user_flow.create_user(profile)
profile.back_button.click()
device_1_home, device_2_home = device_1.get_home_view(), device_2.get_home_view()
device_2_public_key = user_flow.get_public_key(device_2_home)
user_flow.add_contact(device_1_home, device_2_public_key)
device_1_chat = device_1_home.get_chat_view()
device_1_chat.chat_message_input.send_keys('test123')
device_1_chat.send_message_button.click()
device_1_chat.back_button.click()
device_2_home.element_by_text('test123', 'button').click()
device_2_chat = device_2_home.get_chat_view()
device_2_chat.add_to_contacts.click()
device_2_chat.back_button.click()
device_1_profile, device_2_profile = device_1_chat.profile_button.click(), device_2_chat.profile_button.click()
for profile in device_1_profile, device_2_profile:
user_details = user_flow.get_new_username_and_status(profile)
profile.back_button.click()
discover = profile.discover_button.click()
discover.all_popular.click()
discover.find_full_text(user_details['name'])
discover.find_full_text(' %s' % user_details['status'])
discover.back_button.click()
discover.all_recent.click()
discover.find_full_text(user_details['name'])
discover.find_full_text('%s ' % user_details['status'])
@pytest.mark.chat
def test_one_to_one_chat(self):
device_1, device_2 = ConsoleView(self.driver_1), ConsoleView(self.driver_2)
for device in device_1, device_2:
user_flow.create_user(device)
device.back_button.click()
self.create_drivers(2)
device_1, device_2 = \
ConsoleView(self.drivers[0]), ConsoleView(self.drivers[1])
for console in device_1, device_2:
console.create_user()
console.back_button.click()
device_1_home, device_2_home = device_1.get_home_view(), device_2.get_home_view()
device_1_public_key = user_flow.get_public_key(device_1_home)
user_flow.add_contact(device_2_home, device_1_public_key)
device_1_public_key = device_1_home.get_public_key()
device_2_home.add_contact(device_1_public_key)
device_2_chat = device_2_home.get_chat_view()
message_1 = 'SOMETHING'
device_2_chat.chat_message_input.send_keys(message_1)
device_2_chat.send_message_button.click()
message_2 = 'another SOMETHING'
device_1_home.home_button.click()
device_1_home.find_full_text(message_1)
@ -70,15 +35,16 @@ class TestMultipleDevices(MultipleDeviceTestCase):
@pytest.mark.chat
def test_group_chat_send_receive_messages_and_remove_user(self):
device_1, device_2 = ConsoleView(self.driver_1), \
ConsoleView(self.driver_2)
for device in device_1, device_2:
user_flow.create_user(device)
device.back_button.click()
self.create_drivers(2)
device_1, device_2 = \
ConsoleView(self.drivers[0]), ConsoleView(self.drivers[1])
for console in device_1, device_2:
console.create_user()
console.back_button.click()
device_1_home = device_1.get_home_view()
device_2_home = device_2.get_home_view()
device_1_public_key = user_flow.get_public_key(device_1_home)
user_flow.add_contact(device_2_home, device_1_public_key)
device_1_public_key = device_1_home.get_public_key()
device_2_home.add_contact(device_1_public_key)
device_2_chat = device_2_home.get_chat_view()
device_1_user_name = device_2_chat.user_name_text.text
device_2_home.back_button.click(times_to_click=3)
@ -86,7 +52,7 @@ class TestMultipleDevices(MultipleDeviceTestCase):
message_1 = 'first SOMETHING'
message_2 = 'second SOMETHING'
message_3 = 'third SOMETHING'
user_flow.create_group_chat(device_2_home, device_1_user_name, chat_name)
device_2_home.create_group_chat([device_1_user_name], chat_name)
# send_and_receive_messages
device_2_chat.chat_message_input.send_keys(message_1)
@ -100,7 +66,7 @@ class TestMultipleDevices(MultipleDeviceTestCase):
device_2_chat.find_full_text(message_2)
# remove user
device_2_chat.group_chat_options.click()
device_2_chat.chat_options.click()
device_2_chat.chat_settings.click()
for _ in range(2):
try:
@ -126,22 +92,22 @@ class TestMultipleDevices(MultipleDeviceTestCase):
],
ids=['group_chat', 'one_to_one_chat'])
def test_send_funds_via_request(self, test, recipient, sender):
device_1, device_2 = ConsoleView(self.driver_1), ConsoleView(self.driver_2)
user_flow.recover_access(device_1,
passphrase=recipient['passphrase'],
password=recipient['password'],
username=recipient['username'])
user_flow.recover_access(device_2,
passphrase=sender['passphrase'],
password=sender['password'],
username=sender['username'])
self.create_drivers(2)
device_1, device_2 = \
ConsoleView(self.drivers[0]), ConsoleView(self.drivers[1])
device_1.recover_access(passphrase=recipient['passphrase'],
password=recipient['password'],
username=recipient['username'])
device_2.recover_access(passphrase=sender['passphrase'],
password=sender['password'],
username=sender['username'])
device_2_home = device_2.get_home_view()
device_1_home = device_1.get_home_view()
user_flow.add_contact(device_1_home, sender['public_key'])
device_1_home.add_contact(sender['public_key'])
device_1_home.back_button.click(times_to_click=3)
if test == 'group_chat':
group_chat_name = 'gtr_%s' % get_current_time()
user_flow.create_group_chat(device_1_home, sender['username'], group_chat_name)
device_1_home.create_group_chat([sender['username']], group_chat_name)
device_2_home.element_by_text(group_chat_name, 'button').click()
else:
one_to_one_chat_device_1 = device_1_home.element_by_text_part(sender['username'][:25], 'button')
@ -166,7 +132,7 @@ class TestMultipleDevices(MultipleDeviceTestCase):
device_2_chat.element_by_text_part('Requesting %s ETH' % amount, 'button').click()
device_2_chat.send_message_button.click()
device_2_send_transaction = device_2_chat.get_send_transaction_view()
device_2_send_transaction.sign_transaction_button.click()
device_2_send_transaction.try_to_sing_transaction()
device_2_send_transaction.enter_password_input.send_keys(sender['password'])
device_2_send_transaction.sign_transaction_button.click()
device_2_send_transaction.got_it_button.click()
@ -177,5 +143,3 @@ class TestMultipleDevices(MultipleDeviceTestCase):
transaction_element = transactions_view.transactions_table.find_transaction(amount=amount)
transaction_details_view = transaction_element.click()
transaction_hash = transaction_details_view.get_transaction_hash()
api_requests.find_transaction_on_ropsten(address=sender['address'],
transaction_hash=transaction_hash)

View File

@ -1,7 +1,6 @@
import pytest
from itertools import combinations_with_replacement
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase
from tests import user_flow
from views.console_view import ConsoleView
from selenium.common.exceptions import TimeoutException
@ -13,7 +12,7 @@ class TestNetwork(SingleDeviceTestCase):
'Mainnet', 'Mainnet with upstream RPC'])
def test_network_switch(self, network):
console = ConsoleView(self.driver)
user_flow.create_user(console)
console.create_user()
console.back_button.click()
profile_view = console.profile_button.click()
sign_in_view = profile_view.switch_network(network)
@ -32,9 +31,10 @@ class TestNetworkChats(MultipleDeviceTestCase):
@pytest.mark.parametrize("network", network_combinations,
ids=[i[0] + ' & ' + i[1] for i in network_combinations])
def test_one_to_one_chat_between(self, network):
device_1, device_2 = ConsoleView(self.driver_1), ConsoleView(self.driver_2)
for device in device_1, device_2:
user_flow.create_user(device)
self.create_drivers(2)
device_1, device_2 = ConsoleView(self.drivers[0]), ConsoleView(self.drivers[1])
for console in device_1, device_2:
console.create_user()
device_1.back_button.click()
device_1_profile_view = device_1.profile_button.click()
device_1_public_key = device_1_profile_view.public_key_text.text
@ -46,7 +46,6 @@ class TestNetworkChats(MultipleDeviceTestCase):
login_d1.find_full_text('Wallet', 60)
else:
device_1_profile_view.back_button.click()
device_2.back_button.click()
device_2_home_view = device_2.get_home_view()
if network[1] != 'Ropsten with upstream RPC':
@ -56,7 +55,7 @@ class TestNetworkChats(MultipleDeviceTestCase):
device_2_sign_in.password_input.send_keys('qwerty1234')
device_2_home_view = device_2_sign_in.sign_in_button.click()
device_2_home_view.find_full_text('Wallet', 60)
user_flow.add_contact(device_2_home_view, device_1_public_key)
device_2_home_view.add_contact(device_1_public_key)
device_2_chat = device_2.get_chat_view()
message_1 = network[0]
message_2 = network[1]

View File

@ -1,13 +1,106 @@
import pytest
from tests import transaction_users, transaction_users_wallet
from tests.base_test_case import SingleDeviceTestCase
from views.console_view import ConsoleView
from tests import user_flow
from tests import basic_user
@pytest.mark.all
class TestSanity(SingleDeviceTestCase):
@pytest.mark.profile
def test_change_user_name(self):
console_view = ConsoleView(self.driver)
console_view.create_user()
console_view.back_button.click()
profile_view = console_view.profile_button.click()
profile_view.edit_button.click()
profile_view.username_input.clear()
new_username = 'NewUserName!'
profile_view.username_input.send_keys(new_username)
profile_view.confirm_button.click()
sign_in_view = profile_view.logout_button.click()
sign_in_view.first_account_button.click()
sign_in_view.password_input.send_keys('qwerty1234')
home_view = sign_in_view.sign_in_button.click()
home_view.find_full_text('Wallet', 60)
home_view.profile_button.click()
profile_view.edit_button.click()
profile_view.find_full_text(new_username, 5)
@pytest.mark.recover
def test_recover_access(self):
console_view = ConsoleView(self.driver)
console_view.create_user()
console_view.back_button.click()
profile_view = console_view.profile_button.click()
sign_in_view = profile_view.logout_button.click()
recover_access_view = sign_in_view.recover_access_button.click()
recover_access_view.passphrase_input.send_keys(basic_user['passphrase'])
recover_access_view.password_input.send_keys(basic_user['password'])
recover_access_view.confirm_recover_access.click()
recovered_user = sign_in_view.element_by_text(basic_user['username'], 'button')
recovered_user.click()
sign_in_view.password_input.send_keys(basic_user['password'])
sign_in_view.sign_in_button.click()
console_view.find_full_text('Wallet', 60)
if basic_user['password'] in str(console_view.logcat):
pytest.fail('Password in logcat!!!', pytrace=False)
@pytest.mark.gorup_chat
def test_group_chat_members(self):
console_view = ConsoleView(self.driver)
console_view.create_user()
console_view.back_button.click()
home_view = console_view.get_home_view()
users = [transaction_users_wallet['A_USER'], transaction_users_wallet['B_USER'],
transaction_users['A_USER'], transaction_users['B_USER'], basic_user]
user_names = sorted([user['username'] for user in users])
for user in users:
home_view.add_contact(user['public_key'])
console_view.back_button.click(3)
home_view.create_group_chat(sorted([user['username'] for user in users]))
group_chat = home_view.get_chat_view()
group_chat.chat_options.click()
group_chat.chat_settings.click()
group_chat.confirm()
group_chat.more_users_button.click()
for username in user_names:
group_chat.find_full_text(username, 10)
def test_commands_on_second_app_run(self):
console_view = ConsoleView(self.driver)
console_view.create_user()
console_view.back_button.click()
home_view = console_view.get_home_view()
home_view.plus_button.click()
contact_jarrad = home_view.element_by_text('Jarrad', 'button')
contact_jarrad.scroll_to_element()
contact_jarrad.click()
chat_view = home_view.get_chat_view()
commands = '/request', '/send'
for command in commands:
chat_view.find_full_text(command, 2)
self.driver.close_app()
console_view.apps_button.click()
console_view.status_app_icon.scroll_to_element()
console_view.status_app_icon.click()
console_view.ok_button_apk.click()
sign_in_view = console_view.get_sign_in_view()
sign_in_view.first_account_button.click()
sign_in_view.password_input.send_keys('qwerty1234')
sign_in_view.sign_in_button.click()
contact_jarrad.wait_for_element(30)
contact_jarrad.click()
for command in commands:
chat_view.find_full_text(command, 2)
chat_view.back_button.click()
home_view.create_group_chat(['Jarrad'])
@pytest.mark.sign_in
@pytest.mark.parametrize("verification", ["invalid", "valid"])
def test_sign_in(self, verification):
@ -18,7 +111,7 @@ class TestSanity(SingleDeviceTestCase):
{"input": "12345ewq",
"outcome": "Wrong password"}}
console_view = ConsoleView(self.driver)
user_flow.create_user(console_view)
console_view.create_user()
console_view.back_button.click()
profile_view = console_view.profile_button.click()
profile_view.logout_button.scroll_to_element()
@ -54,46 +147,3 @@ class TestSanity(SingleDeviceTestCase):
console.find_full_text(verifications[verification]["outcome"])
if verifications[verification]["input"] in str(console.logcat):
pytest.fail('Password in logcat!!!', pytrace=False)
@pytest.mark.profile
def test_change_profile_name_and_status(self):
new_status = '#newstatus'
new_username = 'NewUserName!'
console_view = ConsoleView(self.driver)
user_flow.create_user(console_view)
console_view.back_button.click()
profile_view = console_view.profile_button.click()
profile_view.user_status_box.click()
profile_view.user_status_input.clear()
profile_view.user_status_input.send_keys(new_status)
profile_view.username_input.click()
profile_view.username_input.clear()
profile_view.username_input.send_keys(new_username)
profile_view.save_button.click()
sign_in_view = profile_view.logout_button.click()
sign_in_view.first_account_button.click()
sign_in_view.password_input.send_keys('qwerty1234')
home_view = sign_in_view.sign_in_button.click()
home_view.find_full_text('Wallet', 60)
home_view.profile_button.click()
for text in new_status + ' ', new_username:
home_view.find_full_text(text, 5)
@pytest.mark.recover
def test_recover_access(self):
console_view = ConsoleView(self.driver)
user_flow.create_user(console_view)
console_view.back_button.click()
profile_view = console_view.profile_button.click()
sign_in_view = profile_view.logout_button.click()
recover_access_view = sign_in_view.recover_access_button.click()
recover_access_view.passphrase_input.send_keys(basic_user['passphrase'])
recover_access_view.password_input.send_keys(basic_user['password'])
recover_access_view.confirm_recover_access.click()
recovered_user = sign_in_view.element_by_text(basic_user['username'], 'button')
recovered_user.click()
sign_in_view.password_input.send_keys(basic_user['password'])
sign_in_view.sign_in_button.click()
console_view.find_full_text('Wallet', 60)
if basic_user['password'] in str(console_view.logcat):
pytest.fail('Password in logcat!!!', pytrace=False)

View File

@ -2,7 +2,7 @@ import pytest
import time
from views.console_view import ConsoleView
from tests.base_test_case import SingleDeviceTestCase
from tests import user_flow, transaction_users, api_requests, get_current_time
from tests import transaction_users, api_requests, get_current_time
from selenium.common.exceptions import TimeoutException
@ -18,21 +18,22 @@ class TestTransactions(SingleDeviceTestCase):
'wrong_password'])
def test_transaction_send_command(self, test, recipient):
console_view = ConsoleView(self.driver)
user_flow.create_user(console_view)
console_view.create_user()
console_view.back_button.click()
home_view = console_view.get_home_view()
recipient_address = transaction_users[recipient]['address']
recipient_key = transaction_users[recipient]['public_key']
transaction_amount = '0.001'
sender_address = user_flow.get_address(home_view)
sender_public_key = home_view.get_public_key()
sender_address = home_view.public_key_to_address(sender_public_key)
home_view.home_button.click()
api_requests.get_donate(sender_address)
initial_balance_recipient = api_requests.get_balance(recipient_address)
user_flow.add_contact(home_view, recipient_key)
home_view.add_contact(recipient_key)
if test == 'group_chat':
home_view.back_button.click(times_to_click=3)
user_flow.create_group_chat(home_view, transaction_users[recipient]['username'],
home_view.create_group_chat([transaction_users[recipient]['username']],
'trg_%s' % get_current_time())
chat_view = home_view.get_chat_view()
else:
@ -67,10 +68,9 @@ class TestTransactions(SingleDeviceTestCase):
@pytest.mark.transaction
def test_send_transaction_from_daap(self):
console = ConsoleView(self.driver)
user_flow.recover_access(console,
transaction_users['B_USER']['passphrase'],
transaction_users['B_USER']['password'],
transaction_users['B_USER']['username'])
console.recover_access(transaction_users['B_USER']['passphrase'],
transaction_users['B_USER']['password'],
transaction_users['B_USER']['username'])
home_view = console.get_home_view()
address = transaction_users['B_USER']['address']
initial_balance = api_requests.get_balance(address)

View File

@ -1,5 +1,5 @@
import pytest
from tests import api_requests, user_flow, transaction_users_wallet
from tests import api_requests, transaction_users_wallet
from tests.base_test_case import SingleDeviceTestCase
from views.console_view import ConsoleView
@ -10,7 +10,7 @@ class TestWallet(SingleDeviceTestCase):
@pytest.mark.wallet
def test_wallet_error_messages(self):
console = ConsoleView(self.driver)
user_flow.create_user(console)
console.create_user()
console.back_button.click()
wallet_view = console.wallet_button.click()
send_transaction = wallet_view.send_button.click()
@ -22,13 +22,12 @@ class TestWallet(SingleDeviceTestCase):
@pytest.mark.wallet
def test_request_transaction_from_wallet(self):
console_view = ConsoleView(self.driver)
user_flow.recover_access(console_view,
transaction_users_wallet['A_USER']['passphrase'],
transaction_users_wallet['A_USER']['password'],
transaction_users_wallet['A_USER']['username'])
console_view.recover_access(transaction_users_wallet['A_USER']['passphrase'],
transaction_users_wallet['A_USER']['password'],
transaction_users_wallet['A_USER']['username'])
home_view = console_view.get_home_view()
recipient_key = transaction_users_wallet['B_USER']['public_key']
user_flow.add_contact(home_view, recipient_key)
home_view.add_contact(recipient_key)
home_view.back_button.click(times_to_click=3)
wallet_view = home_view.wallet_button.click()
send_transaction_view = wallet_view.request_button.click()
@ -43,15 +42,14 @@ class TestWallet(SingleDeviceTestCase):
ids=['sign_now','sign_later'])
def test_send_transaction_from_wallet(self, test, recipient, sender):
console_view = ConsoleView(self.driver)
user_flow.recover_access(console_view,
transaction_users_wallet[sender]['passphrase'],
transaction_users_wallet[sender]['password'],
transaction_users_wallet[sender]['username'])
console_view.recover_access(transaction_users_wallet[sender]['passphrase'],
transaction_users_wallet[sender]['password'],
transaction_users_wallet[sender]['username'])
home_view = console_view.get_home_view()
recipient_key = transaction_users_wallet[recipient]['public_key']
recipient_address = transaction_users_wallet[recipient]['address']
initial_balance_recipient = api_requests.get_balance(recipient_address)
user_flow.add_contact(home_view, recipient_key)
home_view.add_contact(recipient_key)
home_view.back_button.click(times_to_click=3)
wallet_view = home_view.wallet_button.click()
send_transaction = wallet_view.send_button.click()
@ -83,18 +81,15 @@ class TestWallet(SingleDeviceTestCase):
transactions_view = wallet_view.transactions_button.click()
transaction = transactions_view.transactions_table.find_transaction(amount=amount)
details_view = transaction.click()
transaction_hash = details_view.get_transaction_hash()
api_requests.find_transaction_on_ropsten(address=transaction_users_wallet[sender]['address'],
transaction_hash=transaction_hash)
details_view.get_transaction_hash()
@pytest.mark.wallet
def test_eth_and_currency_balance(self):
errors = list()
console = ConsoleView(self.driver)
user_flow.recover_access(console,
passphrase=transaction_users_wallet['A_USER']['passphrase'],
password=transaction_users_wallet['A_USER']['password'],
username=transaction_users_wallet['A_USER']['username'])
console.recover_access(passphrase=transaction_users_wallet['A_USER']['passphrase'],
password=transaction_users_wallet['A_USER']['password'],
username=transaction_users_wallet['A_USER']['username'])
home_view = console.get_home_view()
wallet = home_view.wallet_button.click()
address = transaction_users_wallet['A_USER']['address']

View File

@ -1,70 +0,0 @@
from tests import get_current_time
def create_user(console):
console.request_password_icon.click()
console.chat_request_input.send_keys("qwerty1234")
console.confirm()
console.chat_request_input.send_keys("qwerty1234")
console.confirm()
console.find_full_text(
"Here is your signing phrase. You will use it to verify your transactions. Write it down and keep it safe!")
def recover_access(console, passphrase, password, username):
recover_access_view = console.recover_button.click()
recover_access_view.passphrase_input.send_keys(passphrase)
recover_access_view.password_input.send_keys(password)
recover_access_view.confirm_recover_access.click()
recovered_user = recover_access_view.element_by_text(username, 'button')
recover_access_view.confirm()
recovered_user.click()
recover_access_view.password_input.send_keys(password)
recover_access_view.sign_in_button.click()
recover_access_view.find_full_text('Wallet', 60)
def get_public_key(home):
profile_view = home.profile_button.click()
return profile_view.public_key_text.text
def get_address(home):
profile_view = home.profile_button.click()
return profile_view.profile_address_text.text
def add_contact(home, public_key):
start_new_chat = home.plus_button.click()
start_new_chat.add_new_contact.click()
start_new_chat.public_key_edit_box.send_keys(public_key)
start_new_chat.confirm()
start_new_chat.confirm_public_key_button.click()
def create_group_chat(home, username_to_add, group_chat_name='new_group_chat'):
start_new_chat = home.plus_button.click()
start_new_chat.new_group_chat_button.click()
user_contact = start_new_chat.element_by_text(username_to_add, 'button')
user_contact.scroll_to_element()
user_contact.click()
start_new_chat.next_button.click()
start_new_chat.name_edit_box.send_keys(group_chat_name)
start_new_chat.save_button.click()
def get_new_username_and_status(profile):
users_details = dict()
current_time = get_current_time()
new_status = '#newstatus_%s' % current_time
new_username = 'New_User_Name_%s' % current_time
profile.user_status_box.click()
profile.user_status_input.clear()
profile.user_status_input.send_keys(new_status)
profile.username_input.clear()
profile.username_input.send_keys(new_username)
profile.save_button.click()
users_details['status'] = new_status
users_details['name'] = new_username
return users_details

View File

@ -1,9 +1,9 @@
from appium.webdriver.common.mobileby import MobileBy
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from appium.webdriver.common.touch_action import TouchAction
import logging
from tests import info
class BaseElement(object):
@ -37,25 +37,33 @@ class BaseElement(object):
return None
def find_element(self):
self.info('Looking for %s' % self.name)
return self.driver.find_element(self.locator.by, self.locator.value)
info('Looking for %s' % self.name)
try:
return self.driver.find_element(self.locator.by, self.locator.value)
except NoSuchElementException as exception:
exception.msg = "'%s' is not found on screen, using: '%s'" % (self.name, self.locator)
raise exception
def find_elements(self):
self.info('Looking for %s' % self.name)
info('Looking for %s' % self.name)
return self.driver.find_elements(self.locator.by, self.locator.value)
def wait_for_element(self, seconds=10):
return WebDriverWait(self.driver, seconds)\
.until(expected_conditions.presence_of_element_located((self.locator.by, self.locator.value)))
try:
return WebDriverWait(self.driver, seconds)\
.until(expected_conditions.presence_of_element_located((self.locator.by, self.locator.value)))
except TimeoutException as exception:
exception.msg = "'%s' is not found on screen, using: '%s', during '%s' seconds" % (self.name, self.locator,
seconds)
raise exception
def scroll_to_element(self):
action = TouchAction(self.driver)
for _ in range(5):
for _ in range(9):
try:
return self.find_element()
except NoSuchElementException:
self.info('Scrolling to %s' % self.name)
action.press(x=0, y=1000).move_to(x=200, y=-1000).release().perform()
info('Scrolling down to %s' % self.name)
self.driver.swipe(500, 1000, 500, 500)
def is_element_present(self, sec=5):
try:
@ -68,10 +76,6 @@ class BaseElement(object):
def text(self):
return self.find_element().text
def info(self, text):
if not "Base" in text:
logging.info(text)
class BaseEditBox(BaseElement):
@ -80,19 +84,19 @@ class BaseEditBox(BaseElement):
def send_keys(self, value):
self.find_element().send_keys(value)
self.info("Type '%s' to %s" % (value, self.name))
info("Type '%s' to %s" % (value, self.name))
def set_value(self, value):
self.find_element().set_value(value)
self.info("Type '%s' to %s" % (value, self.name))
info("Type '%s' to %s" % (value, self.name))
def clear(self):
self.find_element().clear()
self.info('Clear text in %s' % self.name)
info('Clear text in %s' % self.name)
def click(self):
self.find_element().click()
self.info('Tap on %s' % self.name)
info('Tap on %s' % self.name)
class BaseText(BaseElement):
@ -103,7 +107,7 @@ class BaseText(BaseElement):
@property
def text(self):
text = self.find_element().text
self.info('%s is %s' % (self.name, text))
info('%s is %s' % (self.name, text))
return text
@ -114,5 +118,5 @@ class BaseButton(BaseElement):
def click(self):
self.find_element().click()
self.info('Tap on %s' % self.name)
info('Tap on %s' % self.name)
return self.navigate()

View File

@ -1,9 +1,12 @@
import logging
import time
import base64
import zbarlight
from tests import info
from eth_keys import datatypes
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from PIL import Image
from datetime import datetime
from io import BytesIO
from views.base_element import BaseButton, BaseElement, BaseEditBox, BaseText
@ -16,9 +19,9 @@ class BackButton(BaseButton):
for _ in range(times_to_click):
try:
self.find_element().click()
info('Tap on %s' % self.name)
except (NoSuchElementException, TimeoutException):
pass
logging.info('Tap on %s' % self.name)
return self.navigate()
@ -31,9 +34,9 @@ class AllowButton(BaseButton):
try:
for _ in range(3):
self.find_element().click()
info('Tap on %s' % self.name)
except NoSuchElementException:
pass
logging.info('Tap on %s' % self.name)
class DenyButton(BaseButton):
@ -110,6 +113,13 @@ class NextButton(BaseButton):
"//android.widget.TextView[@text='NEXT']")
class DoneButton(BaseButton):
def __init__(self, driver):
super(DoneButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
"//android.widget.TextView[@text='Done']")
class AppsButton(BaseButton):
def __init__(self, driver):
super(AppsButton, self).__init__(driver)
@ -141,6 +151,7 @@ class BaseView(object):
self.ok_button_apk = OkButton(self.driver)
self.next_button = NextButton(self.driver)
self.save_button = SaveButton(self.driver)
self.done_button = DoneButton(self.driver)
self.apps_button = AppsButton(self.driver)
self.status_app_icon = StatusAppIcon(self.driver)
@ -157,7 +168,7 @@ class BaseView(object):
return self.driver.get_log("logcat")
def confirm(self):
logging.info("Tap 'Confirm' on native keyboard")
info("Tap 'Confirm' on native keyboard")
self.driver.keyevent(66)
def send_as_keyevent(self, string):
@ -170,30 +181,30 @@ class BaseView(object):
'k': 39, 'l': 40, 'm': 41, 'n': 42, 'o': 43, 'p': 44, 'q': 45, 'r': 46, 's': 47, 't': 48,
'u': 49, 'v': 50, 'w': 51, 'x': 52, 'y': 53, 'z': 54}
for i in string:
logging.info("Tap '%s' on native keyboard" % i)
info("Tap '%s' on native keyboard" % i)
time.sleep(1)
self.driver.keyevent(keys[i])
def find_full_text(self, text, wait_time=60):
logging.info("Looking for full text: '%s'" % text)
info("Looking for full text: '%s'" % text)
element = BaseElement(self.driver)
element.locator = element.Locator.xpath_selector('//*[@text="' + text + '"]')
return element.wait_for_element(wait_time)
def find_text_part(self, text, wait_time=60):
logging.info("Looking for a text part: '%s'" % text)
info("Looking for a text part: '%s'" % text)
element = BaseElement(self.driver)
element.locator = element.Locator.xpath_selector('//*[contains(@text, "' + text + '")]')
return element.wait_for_element(wait_time)
def element_by_text(self, text, element_type='base'):
logging.info("Looking for an element by text: '%s'" % text)
info("Looking for an element by text: '%s'" % text)
element = self.element_types[element_type](self.driver)
element.locator = element.Locator.xpath_selector('//*[@text="' + text + '"]')
return element
def element_by_text_part(self, text, element_type='base'):
logging.info("Looking for an element by text part: '%s'" % text)
info("Looking for an element by text part: '%s'" % text)
element = self.element_types[element_type](self.driver)
element.locator = element.Locator.xpath_selector('//*[contains(@text, "' + text + '")]')
return element
@ -216,3 +227,15 @@ class BaseView(object):
def get_unique_amount(self):
return '0.0%s' % datetime.now().strftime('%-m%-d%-H%-M%-S').strip('0')
def get_text_from_qr(self):
image = Image.open(BytesIO(base64.b64decode(self.driver.get_screenshot_as_base64())))
image.load()
try:
return str(zbarlight.scan_codes('qrcode', image)[0])[2:][:132]
except IndexError:
raise BaseException('No data in QR code')
def public_key_to_address(self, public_key):
raw_public_key = bytearray.fromhex(public_key.replace('0x04', ''))
return datatypes.PublicKey(raw_public_key).to_address()[2:]

View File

@ -1,7 +1,5 @@
import logging
from selenium.common.exceptions import TimeoutException
from tests import info
from views.base_element import BaseButton, BaseEditBox, BaseText
from views.base_view import BaseView
@ -19,7 +17,7 @@ class SendMessageButton(BaseButton):
def click(self):
self.find_element().click()
logging.info('Tap on %s' % self.name)
info('Tap on %s' % self.name)
class AddToContacts(BaseButton):
@ -48,11 +46,17 @@ class RequestCommand(BaseButton):
self.locator = self.Locator.xpath_selector("//*[@text='/request']")
class GroupChatOptions(BaseButton):
class ChatOptions(BaseButton):
def __init__(self, driver):
super(GroupChatOptions, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
"//android.view.ViewGroup[2]//android.widget.TextView[@text='n']")
super(ChatOptions, self).__init__(driver)
self.locator = self.Locator.accessibility_id('chat-menu')
class MembersButton(BaseButton):
def __init__(self, driver):
super(MembersButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector('(//android.view.ViewGroup[@content-desc="action"])[1]')
class ChatSettings(BaseButton):
@ -81,6 +85,19 @@ class FirstRecipient(BaseButton):
"//android.widget.ImageView[@content-desc='chat-icon']")
class MessageByUsername(BaseText):
def __init__(self, driver, username):
super(MessageByUsername, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//*[@text="' + username + '"]'
'/following-sibling::android.widget.TextView')
class MoreUsersButton(BaseButton):
def __init__(self, driver):
super(MoreUsersButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//android.widget.TextView[contains(@text, 'MORE')]")
class ChatView(BaseView):
def __init__(self, driver):
super(ChatView, self).__init__(driver)
@ -93,18 +110,24 @@ class ChatView(BaseView):
self.send_command = SendCommand(self.driver)
self.request_command = RequestCommand(self.driver)
self.group_chat_options = GroupChatOptions(self.driver)
self.chat_options = ChatOptions(self.driver)
self.members_button = MembersButton(self.driver)
self.chat_settings = ChatSettings(self.driver)
self.more_users_button = MoreUsersButton(self.driver)
self.user_options = UserOptions(self.driver)
self.remove_button = RemoveButton(self.driver)
self.first_recipient_button = FirstRecipient(self.driver)
def wait_for_syncing_complete(self):
logging.info('Waiting for syncing complete:')
info('Waiting for syncing complete:')
while True:
try:
sync = self.find_text_part('Syncing', 10)
logging.info(sync.text)
info(sync.text)
except TimeoutException:
break
def get_messages_sent_by_user(self, username):
return MessageByUsername(self.driver, username).find_elements()

View File

@ -1,7 +1,5 @@
import logging
from tests import info
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from views.base_element import BaseButton, BaseEditBox
from views.base_view import BaseView
@ -10,12 +8,12 @@ class RequestPasswordIcon(BaseButton):
def __init__(self, driver):
super(RequestPasswordIcon, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@content-desc='request-password']")
self.locator = self.Locator.accessibility_id('request-password')
def click(self):
self.wait_for_element(10)
self.find_element().click()
logging.info('Tap on %s' % self.name)
info('Tap on %s' % self.name)
return self.navigate()
@ -55,3 +53,24 @@ class ConsoleView(BaseView):
i.click()
except (NoSuchElementException, TimeoutException):
pass
def create_user(self):
self.request_password_icon.click()
self.chat_request_input.send_keys("qwerty1234")
self.confirm()
self.chat_request_input.send_keys("qwerty1234")
self.confirm()
self.find_full_text(
"Here is your signing phrase. You will use it to verify your transactions. Write it down and keep it safe!")
def recover_access(self, passphrase, password, username):
recover_access_view = self.recover_button.click()
recover_access_view.passphrase_input.send_keys(passphrase)
recover_access_view.password_input.send_keys(password)
recover_access_view.confirm_recover_access.click()
recovered_user = recover_access_view.element_by_text(username, 'button')
recover_access_view.confirm()
recovered_user.click()
recover_access_view.password_input.send_keys(password)
recover_access_view.sign_in_button.click()
recover_access_view.find_full_text('Wallet', 30)

View File

@ -1,7 +1,6 @@
import logging
from tests import info
import time
from selenium.common.exceptions import TimeoutException
from views.base_element import BaseButton
from views.base_view import BaseView
@ -42,13 +41,39 @@ class HomeView(BaseView):
self.console_button = ConsoleButton(self.driver)
def wait_for_syncing_complete(self):
logging.info('Waiting for syncing complete:')
info('Waiting for syncing complete:')
while True:
try:
sync = self.find_text_part('Syncing', 10)
logging.info(sync.text)
info(sync.text)
except TimeoutException:
break
def get_chat_with_user(self, username):
return ChatElement(self.driver, username)
def add_contact(self, public_key):
start_new_chat = self.plus_button.click()
start_new_chat.add_new_contact.click()
start_new_chat.public_key_edit_box.send_keys(public_key)
start_new_chat.confirm()
start_new_chat.confirm_public_key_button.click()
def create_group_chat(self, user_names_to_add: list, group_chat_name: str = 'new_group_chat'):
start_new_chat = self.plus_button.click()
start_new_chat.new_group_chat_button.click()
for user_name in user_names_to_add:
user_contact = start_new_chat.element_by_text(user_name, 'button')
user_contact.scroll_to_element()
user_contact.click()
start_new_chat.next_button.click()
start_new_chat.name_edit_box.send_keys(group_chat_name)
start_new_chat.save_button.click()
def get_public_key(self):
profile_view = self.profile_button.click()
profile_view.share_my_contact_key_button.click()
time.sleep(4)
public_key = profile_view.get_text_from_qr()
profile_view.cross_icon.click()
return public_key

View File

@ -19,7 +19,7 @@ class SwitchUsersButton(BaseButton):
def click(self):
self.find_element().click()
logging.info('Tap on %s' % self.name)
info('Tap on %s' % self.name)
return self.navigate()
def navigate(self):

View File

@ -1,5 +1,5 @@
import logging
import time
from tests import info
from views.base_element import BaseText, BaseButton, BaseEditBox
from views.base_view import BaseView
@ -13,7 +13,7 @@ class PublicKeyText(BaseText):
@property
def text(self):
text = self.scroll_to_element().text
logging.info('%s is %s' % (self.name, text))
info('%s is %s' % (self.name, text))
return text
@ -35,20 +35,19 @@ class OptionsButton(BaseButton):
def __init__(self, driver):
super(OptionsButton.UserStatusBox, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
'//android.widget.ImageView[@content-desc="chat-icon"]/..//android.widget.ScrollView')
self.locator = self.Locator.xpath_selector('(//android.widget.ScrollView)[2]//android.widget.TextView')
class UsernameInput(BaseEditBox):
def __init__(self, driver):
super(OptionsButton.UsernameInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//*[@text="Name"]/..//android.widget.EditText')
self.locator = self.Locator.xpath_selector('//android.widget.EditText')
class UserStatusInput(BaseEditBox):
def __init__(self, driver):
super(OptionsButton.UserStatusInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//android.view.ViewGroup[count(*)=1]/android.widget.EditText')
self.locator = self.Locator.xpath_selector('(//android.widget.EditText)[2]')
class NetworkSettingsButton(BaseButton):
@ -73,22 +72,54 @@ class LogoutButton(BaseButton):
def __init__(self, driver):
super(LogoutButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//*[@text="Logout"]')
self.locator = self.Locator.xpath_selector('//*[@text="Log out"]')
def click(self):
self.scroll_to_element()
self.find_element().click()
self.info('Tap on %s' % self.name)
for _ in range(2):
self.find_element().click()
time.sleep(2)
info('Tap on %s' % self.name)
from views.sign_in_view import SignInView
return SignInView(self.driver)
class ShareMyContactKeyButton(BaseButton):
def __init__(self, driver):
super(ShareMyContactKeyButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//*[@text="SHARE MY CONTACT CODE"]')
class EditButton(BaseButton):
def __init__(self, driver):
super(EditButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//*[@text="EDIT"]')
class ConfirmButton(BaseButton):
def __init__(self, driver):
super(ConfirmButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector('(//android.view.ViewGroup[@content-desc="icon"])[1]')
class CrossIcon(BaseButton):
def __init__(self, driver):
super(CrossIcon, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
'(// android.view.ViewGroup[@ content-desc="icon"])[1]/android.view.View')
class ProfileView(BaseView):
def __init__(self, driver):
super(ProfileView, self).__init__(driver)
self.driver = driver
# old design
self.options_button = OptionsButton(self.driver)
self.username_input = OptionsButton.UsernameInput(self.driver)
self.user_status_box = OptionsButton.UserStatusBox(self.driver)
@ -100,6 +131,13 @@ class ProfileView(BaseView):
self.connect_button = NetworkSettingsButton.ConnectButton(self.driver)
self.logout_button = LogoutButton(self.driver)
# new design
self.share_my_contact_key_button = ShareMyContactKeyButton(self.driver)
self.edit_button = EditButton(self.driver)
self.confirm_button = ConfirmButton(self.driver)
self.cross_icon = CrossIcon(self.driver)
def switch_network(self, network):
self.network_settings_button.scroll_to_element()
self.network_settings_button.click()
@ -108,3 +146,7 @@ class ProfileView(BaseView):
self.connect_button.click()
from views.sign_in_view import SignInView
return SignInView(self.driver)
def get_address(self):
profile_view = self.profile_button.click()
return profile_view.profile_address_text.text

View File

@ -1,5 +1,6 @@
from views.base_element import BaseButton, BaseEditBox
from views.base_view import BaseView
from selenium.common.exceptions import NoSuchElementException, TimeoutException
class FirstRecipient(BaseButton):
@ -80,3 +81,11 @@ class SendTransactionView(BaseView):
self.password_input = PasswordInput(self.driver)
self.enter_password_input = EnterPasswordInput(self.driver)
self.got_it_button = GotItButton(self.driver)
def try_to_sing_transaction(self):
for _ in range(4):
try:
self.sign_transaction_button.click()
self.password_input.wait_for_element(5)
except (NoSuchElementException, TimeoutException):
pass

View File

@ -42,13 +42,13 @@ class TransactionTable(BaseElement):
return self.TransactionElement(self.driver, amount=amount)
def find_transaction(self, amount: str) -> TransactionElement:
for i in range(18):
for i in range(9):
try:
element = self.get_transaction_element(amount=amount.replace(',', '.'))
element.find_element()
return element
except NoSuchElementException:
time.sleep(10)
time.sleep(5)
self.driver.swipe(500, 500, 500, 1000)
pytest.fail('Transaction was not found on Wallet/Transaction screen')

View File

@ -1,5 +1,4 @@
import logging
from tests import info
from views.base_view import BaseView
from views.base_element import BaseButton, BaseText
@ -96,4 +95,4 @@ class WalletView(BaseView):
if percentage_diff > 2:
errors.append('Difference between current (%s) and expected (%s) USD balance > 2%%!!' % (usd, expected_usd))
else:
logging.info('Current USD balance %s is ok' % usd)
info('Current USD balance %s is ok' % usd)