diff --git a/test/appium/tests/basetestcase.py b/test/appium/tests/basetestcase.py index 6c898c2cd7..edf1580e9c 100644 --- a/test/appium/tests/basetestcase.py +++ b/test/appium/tests/basetestcase.py @@ -59,7 +59,7 @@ class AbstractTestCase: @property def capabilities_local(self): desired_caps = dict() - desired_caps['deviceName'] = 'takoe' + desired_caps['deviceName'] = 'nexus_5' desired_caps['platformName'] = 'Android' desired_caps['appiumVersion'] = '1.6.5' desired_caps['platformVersion'] = '6.0' diff --git a/test/appium/tests/conftest.py b/test/appium/tests/conftest.py index 1b59d22dbf..3eba1185ae 100644 --- a/test/appium/tests/conftest.py +++ b/test/appium/tests/conftest.py @@ -5,7 +5,7 @@ import re import shutil from datetime import datetime from os import environ -from io import BytesIO +from io import BytesIO, StringIO from sauceclient import SauceClient storage = 'http://artifacts.status.im:8081/artifactory/nightlies-local/' @@ -50,6 +50,8 @@ def is_uploaded(): def pytest_configure(config): + import logging + logging.basicConfig(level=logging.INFO) if is_master(config): if not is_uploaded(): response = requests.get(storage + latest_apk, stream=True) diff --git a/test/appium/tests/test_group_chats.py b/test/appium/tests/test_group_chats.py index ed5596a1d5..8ffc992cbb 100644 --- a/test/appium/tests/test_group_chats.py +++ b/test/appium/tests/test_group_chats.py @@ -71,7 +71,7 @@ class TestGroupChats(MultiplyDeviceTestCase): # chats_d2.find_full_text("You\'ve removed " + user_name_d1) - chats_d2.chat_message_input.send_keys(message_1) + chats_d2.chat_message_input.send_keys(message_3) chats_d2.send_message_button.click() chats_d1.find_text_part("removed you from group chat") diff --git a/test/appium/tests/test_sanity.py b/test/appium/tests/test_sanity.py index f2ef25d99a..9345979d9c 100644 --- a/test/appium/tests/test_sanity.py +++ b/test/appium/tests/test_sanity.py @@ -1,11 +1,70 @@ import pytest from tests.basetestcase import SingleDeviceTestCase from views.home import HomeView +from tests.preconditions import set_password_as_new_user @pytest.mark.sanity class TestSanity(SingleDeviceTestCase): + def test_transaction(self): + home = HomeView(self.driver) + set_password_as_new_user(home) + chats = home.get_chats() + chats.wait_for_syncing_complete() + chats.back_button.click() + chats.profile_button.click() + profile = chats.profile_icon.click() + + sender_address = profile.profile_address_text.text + recipient_key = '0x040e016b940e067997be8d91298d893ff2bc3580504b4ccb155ea03d183b85f1' \ + '8e771a763d99f60fec70edf637eb6bad9f96d3e8a544168d3ad144f83b4cf7625c' + recipient_address = '67a50ef1d26de6d65dbfbb88172ac1e7017e766d' + + profile.get_donate(sender_address) + initial_balance = profile.get_balance(recipient_address) + + profile.back_button.click() + chats.plus_button.click() + chats.add_new_contact.click() + chats.public_key_edit_box.send_keys(recipient_key) + chats.confirm() + chats.confirm_public_key_button.click() + + 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_message_button.click() + chats.confirm_transaction_button.click() + chats.password_input.send_keys('qwerty1234') + chats.confirm_button.click() + chats.got_it_button.click() + chats.find_full_text('0.1') + chats.find_full_text('Sent', 60) + chats.verify_balance_is_updated(initial_balance, recipient_address) + + @pytest.mark.parametrize("verification", ["invalid", "valid"]) + def test_sign_in(self, verification): + + verifications = {"valid": + {"input": "qwerty1234", + "outcome": "Chats"}, + "invalid": + {"input": "12345ewq", + "outcome": "Wrong password"}} + home = HomeView(self.driver) + set_password_as_new_user(home) + chats = home.get_chats() + chats.back_button.click() + chats.profile_button.click() + login = chats.switch_users_button.click() + login.first_account_button.click() + login.password_input.send_keys(verifications[verification]['input']) + login.sign_in_button.click() + home.find_full_text(verifications[verification]["outcome"], 10) + @pytest.mark.parametrize("verification", ["short", "mismatch", "valid"]) def test_password(self, verification): diff --git a/test/appium/views/base_element.py b/test/appium/views/base_element.py index c6e01243b1..69f072cf31 100644 --- a/test/appium/views/base_element.py +++ b/test/appium/views/base_element.py @@ -3,6 +3,7 @@ from selenium.common.exceptions import NoSuchElementException, 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 class BaseElement(object): @@ -36,6 +37,7 @@ class BaseElement(object): return None def find_element(self): + logging.info('Looking for %s' % self.name) return self.driver.find_element(self.locator.by, self.locator.value) def wait_for_element(self, seconds=10): @@ -48,6 +50,7 @@ class BaseElement(object): 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() @@ -58,6 +61,10 @@ class BaseElement(object): except TimeoutException: return False + @property + def text(self): + return self.find_element().text + class BaseEditBox(BaseElement): @@ -66,6 +73,7 @@ class BaseEditBox(BaseElement): def send_keys(self, value): self.find_element().send_keys(value) + logging.info('Type %s to %s' % (value, self.name)) class BaseText(BaseElement): @@ -75,7 +83,9 @@ class BaseText(BaseElement): @property def text(self): - return self.find_element().text + text = self.find_element().text + logging.info('%s is %s' % (self.name, text)) + return text class BaseButton(BaseElement): @@ -85,4 +95,5 @@ class BaseButton(BaseElement): def click(self): self.find_element().click() + logging.info('Tap on %s' % self.name) return self.navigate() diff --git a/test/appium/views/base_view.py b/test/appium/views/base_view.py index 5efbd8224b..2a7a40b845 100644 --- a/test/appium/views/base_view.py +++ b/test/appium/views/base_view.py @@ -1,4 +1,8 @@ from views.base_element import BaseElement, BaseButton, BaseEditBox, BaseText +import logging +import time +import pytest +import requests class BackButton(BaseButton): @@ -8,24 +12,40 @@ class BackButton(BaseButton): self.locator = self.Locator.xpath_selector("//*[@content-desc='toolbar-back-button']") +class ContactsButton(BaseButton): + + def __init__(self, driver): + super(ContactsButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='Contacts']") + + class BaseViewObject(object): def __init__(self, driver): self.driver = driver self.back_button = BackButton(self.driver) + self.contacts_button = ContactsButton(self.driver) def confirm(self): self.driver.keyevent(66) - def find_full_text(self, text): + 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} + self.driver.keyevent(keys[integer]) + + def send_dot_as_keyevent(self): + self.driver.keyevent(55) + + def find_full_text(self, text, wait_time=60): element = BaseElement(self.driver) element.locator = element.Locator.xpath_selector('//*[@text="' + text + '"]') - return element.wait_for_element(60) + return element.wait_for_element(wait_time) - def find_text_part(self, text): + def find_text_part(self, text, wait_time=60): element = BaseElement(self.driver) element.locator = element.Locator.xpath_selector('//*[contains(@text, "' + text + '")]') - return element.wait_for_element(60) + return element.wait_for_element(wait_time) def element_by_text(self, text, element_type='base'): @@ -43,3 +63,36 @@ class BaseViewObject(object): def get_chats(self): from views.chats import ChatsViewObject return ChatsViewObject(self.driver) + + def get_balance(self, address): + url = 'http://ropsten.etherscan.io/api?module=account&action=balance&address=0x%s&tag=latest' % address + return requests.request('GET', url).json()["result"] + + def get_donate(self, address, wait_time=300): + response = requests.request('GET', 'http://46.101.129.137:3001/donate/0x%s' % address).json() + counter = 0 + while True: + if counter == wait_time: + logging.info("Donation was not received during %s seconds!" % wait_time) + break + elif self.get_balance(address) != '1000000000000000000': + counter += 10 + time.sleep(10) + logging.info('Waiting %s seconds for donation' % counter) + else: + logging.info('Got %s for %s' % (response["amount"], address)) + break + + def verify_balance_is_updated(self, initial_balance, recipient_address, wait_time=120): + counter = 0 + while True: + if counter == wait_time: + logging.info('Balance is not changed during %s seconds, funds were not received!') + break + elif initial_balance == self.get_balance(recipient_address): + counter += 10 + time.sleep(10) + logging.info('Waiting %s seconds for funds' % counter) + else: + logging.info('Transaction was received and verified on ropsten.etherscan.io') + break diff --git a/test/appium/views/chats.py b/test/appium/views/chats.py index 7e98b1f758..e39603a56a 100644 --- a/test/appium/views/chats.py +++ b/test/appium/views/chats.py @@ -1,4 +1,6 @@ from views.base_view import BaseViewObject +import pytest +import time from views.base_element import * @@ -20,6 +22,18 @@ class ProfileIcon(BaseButton): return ProfileViewObject(self.driver) +class SwitchUsersButton(BaseButton): + + def __init__(self, driver): + super(SwitchUsersButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='SWITCH USERS']") + + def navigate(self): + from views.login import LoginView + time.sleep(2) + return LoginView(self.driver) + + class PlusButton(BaseButton): def __init__(self, driver): @@ -28,6 +42,14 @@ class PlusButton(BaseButton): "//android.widget.TextView[@text='+']") +class ConsoleButton(BaseButton): + + def __init__(self, driver): + super(ConsoleButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//*[@text='Console']") + + class AddNewContactButton(BaseButton): def __init__(self, driver): @@ -36,6 +58,13 @@ class AddNewContactButton(BaseButton): "//android.widget.TextView[@text='Add new contact']") +class NewContactButton(BaseButton): + + def __init__(self, driver): + super(NewContactButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='']") + + class NewGroupChatButton(BaseButton): def __init__(self, driver): @@ -113,6 +142,11 @@ class SendMessageButton(BaseButton): super(SendMessageButton, self).__init__(driver) self.locator = self.Locator.accessibility_id("send-message-button") + def click(self): + time.sleep(30) + self.find_element().click() + logging.info('Tap on %s' % self.name) + class UserNameText(BaseText): @@ -122,6 +156,46 @@ class UserNameText(BaseText): self.Locator.xpath_selector("//android.widget.ScrollView//android.widget.TextView") +class SendFundsButton(BaseButton): + + def __init__(self, driver): + super(SendFundsButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='/send']") + + class FirstRecipient(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"]') + + class ConfirmTransactionButton(BaseButton): + + def __init__(self, driver): + super(SendFundsButton.ConfirmTransactionButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='CONFIRM TRANSACTION']") + + class PasswordInput(BaseEditBox): + + def __init__(self, driver): + super(SendFundsButton.PasswordInput, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='Password']") + + class ConfirmButton(BaseButton): + + def __init__(self, driver): + super(SendFundsButton.ConfirmButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='CONFIRM']") + + class GotItButton(BaseButton): + + def __init__(self, driver): + super(SendFundsButton.GotItButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='GOT IT']") + + + + class ChatsViewObject(BaseViewObject): def __init__(self, driver): @@ -130,8 +204,11 @@ class ChatsViewObject(BaseViewObject): self.profile_button = ProfileButton(self.driver) self.profile_icon = ProfileIcon(self.driver) + self.switch_users_button = SwitchUsersButton(self.driver) + self.plus_button = PlusButton(self.driver) self.add_new_contact = AddNewContactButton(self.driver) + self.console_button = ConsoleButton(self.driver) self.public_key_edit_box = PublicKeyEditBox(self.driver) self.confirm_public_key_button = ConfirmPublicKeyButton(self.driver) @@ -148,3 +225,21 @@ class ChatsViewObject(BaseViewObject): self.chat_message_input = ChatMessageInput(self.driver) self.send_message_button = SendMessageButton(self.driver) self.user_name_text = UserNameText(self.driver) + + self.send_funds_button = SendFundsButton(self.driver) + self.first_recipient_button = SendFundsButton.FirstRecipient(self.driver) + self.confirm_transaction_button = SendFundsButton.ConfirmTransactionButton(self.driver) + self.confirm_button = SendFundsButton.ConfirmButton(self.driver) + self.password_input = SendFundsButton.PasswordInput(self.driver) + self.got_it_button = SendFundsButton.GotItButton(self.driver) + + self.new_contact_button = NewContactButton(self.driver) + + def wait_for_syncing_complete(self): + logging.info('Waiting for syncing complete:') + while True: + try: + sync = self.find_text_part('Syncing', 10) + logging.info(sync.text) + except TimeoutException: + break diff --git a/test/appium/views/login.py b/test/appium/views/login.py new file mode 100644 index 0000000000..9221a3b6dc --- /dev/null +++ b/test/appium/views/login.py @@ -0,0 +1,35 @@ +from views.base_view import BaseViewObject +import pytest +from views.base_element import * + + +class FirstAccountButton(BaseButton): + + def __init__(self, driver): + super(FirstAccountButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.ScrollView//android.widget.TextView") + + +class PasswordInput(BaseEditBox): + + def __init__(self, driver): + super(PasswordInput, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.EditText") + + +class SignInButton(BaseButton): + + def __init__(self, driver): + super(SignInButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@text='Sign in']") + + +class LoginView(BaseViewObject): + + def __init__(self, driver): + super(LoginView, self).__init__(driver) + self.driver = driver + + self.first_account_button = FirstAccountButton(self.driver) + self.password_input = PasswordInput(self.driver) + self.sign_in_button = SignInButton(self.driver) diff --git a/test/appium/views/profile.py b/test/appium/views/profile.py index 21b1548469..1268d48026 100644 --- a/test/appium/views/profile.py +++ b/test/appium/views/profile.py @@ -9,6 +9,13 @@ class PublicKeyText(BaseText): self.locator = self.Locator.accessibility_id('profile-public-key') +class ProfileAddressText(BaseText): + + def __init__(self, driver): + super(ProfileAddressText, self).__init__(driver) + self.locator = self.Locator.accessibility_id('profile-address') + + class ProfileViewObject(BaseViewObject): def __init__(self, driver): @@ -16,3 +23,4 @@ class ProfileViewObject(BaseViewObject): self.driver = driver self.public_key_text = PublicKeyText(self.driver) + self.profile_address_text = ProfileAddressText(self.driver)