added 'send transaction initiated from the DApp' and 'send transaction with invalid password' tests, basetestcase reworked for smooth local runs

added 'send transaction initiated from the DApp' and 'send transaction with invalid password' tests, basetestcase reworked for smooth local runs
This commit is contained in:
Anton Danchenko 2017-10-05 22:41:17 +03:00
parent d7fcb333da
commit 98beef138f
11 changed files with 285 additions and 65 deletions

View File

@ -6,7 +6,10 @@ from appium import webdriver
from abc import ABCMeta, \
abstractmethod
import hmac
import re
import subprocess
from hashlib import md5
from selenium.common.exceptions import WebDriverException
class AbstractTestCase:
@ -26,21 +29,8 @@ class AbstractTestCase:
return 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (self.sauce_username, self.sauce_access_key)
@property
def capabilities_sauce_lab(self):
desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.6.5'
desired_caps['platformVersion'] = '6.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['app'] = pytest.config.getoption('apk')
desired_caps['browserName'] = ''
desired_caps['deviceOrientation'] = "portrait"
desired_caps['name'] = tests_data.name
desired_caps['build'] = pytest.config.getoption('build')
desired_caps['idleTimeout'] = 1000
desired_caps['commandTimeout'] = 600
return desired_caps
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'),
@ -53,18 +43,42 @@ class AbstractTestCase:
pytest.config.getoption('build')))
print(self.get_public_url(driver))
def add_local_devices_to_capabilities(self):
updated_capabilities = list()
raw_out = re.split(r'[\r\\n]+', str(subprocess.check_output(['adb', 'devices'])).rstrip())
for line in raw_out[1:]:
serial = re.findall(r"([\d.\d:]*\d+)", line)
if serial:
capabilities = self.capabilities_local
capabilities['udid'] = serial[0]
updated_capabilities.append(capabilities)
return updated_capabilities
@property
def executor_local(self):
return 'http://localhost:4723/wd/hub'
def capabilities_sauce_lab(self):
desired_caps = dict()
desired_caps['app'] = pytest.config.getoption('apk')
desired_caps['build'] = pytest.config.getoption('build')
desired_caps['name'] = tests_data.name
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.7.1'
desired_caps['platformVersion'] = '6.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 1000
return desired_caps
@property
def capabilities_local(self):
desired_caps = dict()
desired_caps['app'] = pytest.config.getoption('apk')
desired_caps['deviceName'] = 'nexus_5'
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.6.5'
desired_caps['appiumVersion'] = '1.7.1'
desired_caps['platformVersion'] = '6.0'
desired_caps['app'] = pytest.config.getoption('apk')
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 1000
return desired_caps
@abstractmethod
@ -75,40 +89,82 @@ class AbstractTestCase:
def teardown_method(self, method):
raise NotImplementedError('Should be overridden from a child class')
@property
def environment(self):
return pytest.config.getoption('env')
class SingleDeviceTestCase(AbstractTestCase):
class LocalMultiplyDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
self.driver = webdriver.Remote(self.executor_sauce_lab,
self.capabilities_sauce_lab)
self.driver.implicitly_wait(20)
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(10)
def teardown_method(self, method):
self.print_sauce_lab_info(self.driver)
self.driver.quit()
for driver in self.driver_1, self.driver_2:
try:
driver.quit()
except WebDriverException:
pass
class MultiplyDeviceTestCase(AbstractTestCase):
class SauceMultiplyDeviceTestCase(AbstractTestCase):
@classmethod
def setup_class(cls):
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(20)
driver.implicitly_wait(10)
def teardown_method(self, method):
for driver in self.driver_1, self.driver_2:
self.print_sauce_lab_info(driver)
driver.quit()
try:
driver.quit()
except WebDriverException:
pass
@classmethod
def teardown_class(cls):
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(10)
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': LocalMultiplyDeviceTestCase,
'sauce': SauceMultiplyDeviceTestCase}
class MultiplyDeviceTestCase(environments[pytest.config.getoption('env')]):
pass

View File

@ -31,6 +31,10 @@ def pytest_addoption(parser):
action='store',
default='sauce-storage:' + latest_apk,
help='Please provide url or local path to apk')
parser.addoption('--env',
action='store',
default='sauce',
help='Please specify environment: local/sauce')
def pytest_runtest_setup(item):

View File

@ -19,6 +19,9 @@ def recover_access(chats, passphrase, password, username):
login.password_input.send_keys(password)
login.confirm_recover_access.click()
recovered_user = login.element_by_text(username, 'button')
login.confirm()
recovered_user.click()
login.password_input.send_keys(password)
login.sign_in_button.click()
login.find_full_text('Chats', 60)

View File

@ -1,4 +1,5 @@
import pytest
import time
from tests.basetestcase import SingleDeviceTestCase
from views.home import HomeView
from tests.preconditions import set_password_as_new_user, recover_access
@ -6,7 +7,7 @@ from tests import basic_user, transaction_users
@pytest.mark.sanity
class TestSanity(SingleDeviceTestCase):
class TestAccess(SingleDeviceTestCase):
def test_recover_access(self):
home = HomeView(self.driver)
@ -23,7 +24,7 @@ class TestSanity(SingleDeviceTestCase):
recovered_user.click()
login.password_input.send_keys(basic_user['password'])
login.sign_in_button.click()
home.find_full_text('Chats', 10)
home.find_full_text('Chats', 60)
@pytest.mark.parametrize("verification", ["invalid", "valid"])
def test_sign_in(self, verification):
@ -62,9 +63,10 @@ class TestSanity(SingleDeviceTestCase):
home.confirm()
home.find_full_text(verifications[verification]["outcome"])
@pytest.mark.parametrize("test, recipient, sender", [('group_chat', 'A_USER', 'B_USER'),
@pytest.mark.parametrize("test, recipient, sender", [('wrong_password', 'A_USER', 'B_USER'),
('group_chat', 'A_USER', 'B_USER'),
('one_to_one_chat', 'B_USER', 'A_USER')],
ids=['group_chat', 'one_to_one_chat'])
ids=['group_chat', 'one_to_one_chat', 'wrong_password'])
def test_send_transaction(self, test, recipient, sender):
home = HomeView(self.driver)
set_password_as_new_user(home)
@ -102,18 +104,52 @@ class TestSanity(SingleDeviceTestCase):
chats.send_funds_button.click()
chats.first_recipient_button.click()
chats.send_int_as_keyevent(0)
chats.send_dot_as_keyevent()
chats.send_int_as_keyevent(1)
chats.send_as_keyevent('0,1')
chats.send_message_button.click()
chats.confirm_transaction_button.wait_for_element(60)
chats.confirm_transaction_button.click()
chats.password_input.send_keys(transaction_users[sender]['password'])
chats.confirm_button.click()
chats.got_it_button.click()
chats.sign_transaction_button.wait_for_element(20)
chats.sign_transaction_button.click()
chats.find_full_text('0.1')
chats.find_full_text('Sent', 60)
if test == 'group_chat':
chats.find_full_text('to ' + transaction_users[recipient]['username'], 60)
chats.verify_balance_is_updated(initial_balance_recipient, recipient_address)
if test == 'wrong_password':
chats.enter_password_input.send_keys('invalid')
chats.sign_transaction_button.click()
chats.find_full_text('Wrong password', 20)
else:
chats.enter_password_input.send_keys(transaction_users[recipient]['password'])
chats.sign_transaction_button.click()
chats.find_full_text('0.1')
chats.find_full_text('Sent', 60)
if test == 'group_chat':
chats.find_full_text('to ' + transaction_users[recipient]['username'], 60)
chats.verify_balance_is_updated(initial_balance_recipient, recipient_address)
def test_send_transaction_from_daap(self):
home = HomeView(self.driver)
set_password_as_new_user(home)
chats = home.get_chats()
address = transaction_users['B_USER']['address']
initial_balance = chats.get_balance(address)
recover_access(chats,
transaction_users['B_USER']['passphrase'],
transaction_users['B_USER']['password'],
transaction_users['B_USER']['username'])
if chats.get_balance(address) < 1000000000000000000:
chats.get_donate(address)
contacts = chats.contacts_button.click()
auction_house = contacts.auction_house_button.click()
auction_house.toggle_navigation_button.click()
auction_house.new_auction_button.click()
auction_house.name_to_reserve_input.click()
auction_name = time.strftime('%Y-%m-%d-%H-%M')
auction_house.send_as_keyevent(auction_name)
auction_house.register_name_button.click()
chats.sign_transaction_button.wait_for_element(20)
chats.sign_transaction_button.click()
chats.enter_password_input.send_keys(transaction_users['B_USER']['password'])
chats.sign_transaction_button.click()
auction_house.find_full_text('You are the proud owner of the name: ' + auction_name, 120)
chats.verify_balance_is_updated(initial_balance, address)

View File

@ -40,18 +40,22 @@ class BaseElement(object):
logging.info('Looking for %s' % self.name)
return self.driver.find_element(self.locator.by, self.locator.value)
def find_elements(self):
logging.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)))
def scroll_to_element(self):
action = TouchAction(self.driver)
for _ in range(5):
try:
self.find_element()
break
except NoSuchElementException:
logging.info('Scrolling to %s' % self.name)
action = TouchAction(self.driver)
action.press(x=0, y=1000).move_to(x=200, y=-1000).release().perform()
def is_element_present(self, sec=5):
@ -75,6 +79,10 @@ class BaseEditBox(BaseElement):
self.find_element().send_keys(value)
logging.info('Type %s to %s' % (value, self.name))
def set_value(self, value):
self.find_element().set_value(value)
logging.info('Set %s to %s' % (value, self.name))
class BaseText(BaseElement):

View File

@ -11,6 +11,12 @@ class BackButton(BaseButton):
super(BackButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@content-desc='toolbar-back-button']")
def click(self):
self.wait_for_element(30)
self.find_element().click()
logging.info('Tap on %s' % self.name)
return self.navigate()
class ContactsButton(BaseButton):
@ -18,6 +24,10 @@ class ContactsButton(BaseButton):
super(ContactsButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@text='Contacts']")
def navigate(self):
from views.contacts import ContactsViewObject
return ContactsViewObject(self.driver)
class BaseViewObject(object):
@ -27,23 +37,25 @@ class BaseViewObject(object):
self.contacts_button = ContactsButton(self.driver)
def confirm(self):
logging.info("Tap 'Confirm' on native keyboard")
self.driver.keyevent(66)
def send_int_as_keyevent(self, integer):
keys = {0: 7, 1: 8, 2: 9, 3: 10, 4: 11,
5: 12, 6: 13, 7: 14, 8: 15, 9: 16}
time.sleep(2)
self.driver.keyevent(keys[integer])
def send_dot_as_keyevent(self):
self.driver.keyevent(55)
def send_as_keyevent(self, string):
keys = {'0': 7, '1': 8, '2': 9, '3': 10, '4': 11, '5': 12, '6': 13, '7': 14, '8': 15, '9': 16,
',': 55, '-': 69}
for i in string:
logging.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)
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)
element = BaseElement(self.driver)
element.locator = element.Locator.xpath_selector('//*[contains(@text, "' + text + '")]')
return element.wait_for_element(wait_time)

View File

@ -148,7 +148,6 @@ class SendMessageButton(BaseButton):
self.locator = self.Locator.accessibility_id("send-message-button")
def click(self):
time.sleep(10)
self.find_element().click()
logging.info('Tap on %s' % self.name)
@ -171,14 +170,14 @@ class SendFundsButton(BaseButton):
def __init__(self, driver):
super(SendFundsButton.FirstRecipient, self).__init__(driver)
self.locator = self.Locator.xpath_selector('//android.view.ViewGroup[4]//'
'android.widget.ImageView[@content-desc="chat-icon"]')
self.locator = self.Locator.xpath_selector("//*[@text='Choose recipient']/.."
"//android.widget.ImageView[@content-desc='chat-icon']")
class ConfirmTransactionButton(BaseButton):
class SignTransactionButton(BaseButton):
def __init__(self, driver):
super(SendFundsButton.ConfirmTransactionButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@text='CONFIRM TRANSACTION']")
super(SendFundsButton.SignTransactionButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@text='SIGN TRANSACTION']")
class PasswordInput(BaseEditBox):
@ -186,6 +185,12 @@ class SendFundsButton(BaseButton):
super(SendFundsButton.PasswordInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//*[@text='Password']")
class EnterPasswordInput(BaseEditBox):
def __init__(self, driver):
super(SendFundsButton.EnterPasswordInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//android.widget.EditText[@NAF='true']")
class ConfirmButton(BaseButton):
def __init__(self, driver):
@ -233,9 +238,10 @@ class ChatsViewObject(BaseViewObject):
self.send_funds_button = SendFundsButton(self.driver)
self.first_recipient_button = SendFundsButton.FirstRecipient(self.driver)
self.confirm_transaction_button = SendFundsButton.ConfirmTransactionButton(self.driver)
self.sign_transaction_button = SendFundsButton.SignTransactionButton(self.driver)
self.confirm_button = SendFundsButton.ConfirmButton(self.driver)
self.password_input = SendFundsButton.PasswordInput(self.driver)
self.enter_password_input = SendFundsButton.EnterPasswordInput(self.driver)
self.got_it_button = SendFundsButton.GotItButton(self.driver)
self.new_contact_button = NewContactButton(self.driver)

View File

@ -0,0 +1,24 @@
from views.base_element import BaseElement, BaseButton, BaseEditBox, BaseText
import logging
import time
import pytest
class AuctionHouseButton(BaseButton):
def __init__(self, driver):
super(AuctionHouseButton, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
"(//android.widget.TextView[@text='Auction House'])[1]")
def navigate(self):
from views.web_views.auction_house import AuctionHouseWebView
return AuctionHouseWebView(self.driver)
class ContactsViewObject(object):
def __init__(self, driver):
self.driver = driver
self.auction_house_button = AuctionHouseButton(self.driver)

View File

@ -32,7 +32,7 @@ class RequestPasswordIcon(BaseButton):
self.locator = self.Locator.xpath_selector("//*[@content-desc='request-password']")
def click(self):
self.wait_for_element(60)
self.wait_for_element(10)
self.find_element().click()
logging.info('Tap on %s' % self.name)
return self.navigate()
@ -44,12 +44,10 @@ class HomeView(BaseViewObject):
super(HomeView, self).__init__(driver)
self.continue_button_apk = ContinueButtonAPK(driver)
self.ok_button_apk = OkButtonAPK(driver)
for i in self.ok_button_apk, self.continue_button_apk:
try:
i.click()
except (NoSuchElementException, TimeoutException):
pass
self.chat_request_input = ChatRequestInput(driver)
self.request_password_icon = RequestPasswordIcon(driver)

View File

@ -0,0 +1,43 @@
from views.web_views.base_web_view import *
class ToggleNavigationButton(BaseButton):
def __init__(self, driver):
super(ToggleNavigationButton, self).__init__(driver)
self.locator = self.Locator.accessibility_id('Toggle navigation ')
class NewAuctionButton(BaseButton):
def __init__(self, driver):
super(ToggleNavigationButton.NewAuctionButton, self).__init__(driver)
self.locator = self.Locator.accessibility_id('New Auction')
class ReserveAssetName(BaseElement):
class NameToReserveInput(BaseEditBox, BaseButton):
def __init__(self, driver):
super(ReserveAssetName.NameToReserveInput, self).__init__(driver)
self.locator = self.Locator.xpath_selector(
'(//android.widget.EditText[@content-desc="eg MyFamousWallet.eth"])[1]')
class RegisterNameButton(BaseButton):
def __init__(self, driver):
super(ReserveAssetName.RegisterNameButton, self).__init__(driver)
self.locator = self.Locator.accessibility_id('Register Name')
class AuctionHouseWebView(BaseWebViewObject):
def __init__(self, driver):
super(AuctionHouseWebView, self).__init__(driver)
self.driver = driver
self.wait_for_page_loaded()
self.toggle_navigation_button = ToggleNavigationButton(self.driver)
self.new_auction_button = ToggleNavigationButton.NewAuctionButton(self.driver)
self.name_to_reserve_input = ReserveAssetName.NameToReserveInput(self.driver)
self.register_name_button = ReserveAssetName.RegisterNameButton(self.driver)

View File

@ -0,0 +1,30 @@
from views.base_view import *
class ProgressBarIcon(BaseElement):
def __init__(self, driver):
super(ProgressBarIcon, self).__init__(driver)
self.locator = self.Locator.xpath_selector("//android.widget.ProgressBar")
class BaseWebViewObject(BaseViewObject):
def __init__(self, driver):
super(BaseWebViewObject, self).__init__(driver)
self.driver = driver
self.progress_bar_icon = ProgressBarIcon(self.driver)
def wait_for_page_loaded(self, wait_time=20):
counter = 0
while self.progress_bar_icon.is_element_present(5):
time.sleep(1)
counter += 1
if counter > wait_time:
pytest.fail("Page is not loaded during %s seconds" % wait_time)
def find_full_text(self, text, wait_time=60):
element = BaseElement(self.driver)
element.locator = element.Locator.xpath_selector('//*[@content-desc="' + text + '"]')
return element.wait_for_element(wait_time)