From fb534172954501650a09ced1bfae86a7e6714d83 Mon Sep 17 00:00:00 2001 From: Anton Danchenko Date: Thu, 31 Aug 2017 16:39:41 +0300 Subject: [PATCH] added group chat test and auto retrieving of latest apk --- test/appium/Jenkinsfile | 2 +- test/appium/pytest.ini | 2 +- test/appium/tests/conftest.py | 57 +++++++++++++++++++++-- test/appium/tests/preconditions.py | 2 +- test/appium/tests/test_chats.py | 8 ++-- test/appium/tests/test_group_chats.py | 49 ++++++++++++++++++++ test/appium/views/base_element.py | 19 +++++--- test/appium/views/base_view.py | 4 +- test/appium/views/chats.py | 65 ++++++++++++++++++++++++++- test/appium/views/home.py | 17 +++++-- test/appium/views/profile.py | 9 +--- 11 files changed, 203 insertions(+), 31 deletions(-) create mode 100644 test/appium/tests/test_group_chats.py diff --git a/test/appium/Jenkinsfile b/test/appium/Jenkinsfile index f864bf45a9..f322e4ee51 100644 --- a/test/appium/Jenkinsfile +++ b/test/appium/Jenkinsfile @@ -1,5 +1,5 @@ node {sauce('6269510b-13f3-4019-b156-c2c835f3a408') { - try {sh '/usr/local/bin/python3 -m pytest -m sanity -n5 --apk apkUrl --build ${JOB_NAME}__${BUILD_NUMBER}' + try {sh '/usr/local/bin/python3 -m pytest -m sanity -v --dist=loadscope -n3 --build ${JOB_NAME}__${BUILD_NUMBER}' } finally { saucePublisher() diff --git a/test/appium/pytest.ini b/test/appium/pytest.ini index f6ee4716ba..e0c3511452 100644 --- a/test/appium/pytest.ini +++ b/test/appium/pytest.ini @@ -1,3 +1,3 @@ [pytest] norecursedirs = .git views -addopts = -s -v --junitxml=result.xml --tb=short +addopts = -s --junitxml=result.xml --tb=short diff --git a/test/appium/tests/conftest.py b/test/appium/tests/conftest.py index 888c476806..1b59d22dbf 100644 --- a/test/appium/tests/conftest.py +++ b/test/appium/tests/conftest.py @@ -1,12 +1,63 @@ from tests import tests_data import time +import requests +import re +import shutil +from datetime import datetime +from os import environ +from io import BytesIO +from sauceclient import SauceClient + +storage = 'http://artifacts.status.im:8081/artifactory/nightlies-local/' + +sauce_username = environ.get('SAUCE_USERNAME') +sauce_access_key = environ.get('SAUCE_ACCESS_KEY') + + +def get_latest_apk(): + raw_data = requests.request('GET', storage).text + dates = re.findall("\d{2}-[a-zA-Z]{3}-\d{4} \d{2}:\d{2}", raw_data) + dates.sort(key=lambda date: datetime.strptime(date, "%d-%b-%Y %H:%M"), reverse=True) + return re.findall('>(.*k)\s*%s' % dates[0], raw_data)[0] + +latest_apk = get_latest_apk() def pytest_addoption(parser): - parser.addoption("--build", action="store", default='build_' + time.strftime('%Y_%m_%d_%H_%M'), + parser.addoption("--build", + action="store", + default='build_' + time.strftime('%Y_%m_%d_%H_%M'), help="Specify build name") - parser.addoption('--apk', action='store', default=None, help='Please provide url or local path to apk') + parser.addoption('--apk', + action='store', + default='sauce-storage:' + latest_apk, + help='Please provide url or local path to apk') def pytest_runtest_setup(item): - tests_data.name = item.name + tests_data.name = item.name + '_' + latest_apk + + +def is_master(config): + return not hasattr(config, 'slaveinput') + + +def is_uploaded(): + stored_files = SauceClient(sauce_username, sauce_access_key).storage.get_stored_files() + for i in range(len(stored_files['files'])): + if stored_files['files'][i]['name'] == latest_apk: + return True + + +def pytest_configure(config): + if is_master(config): + if not is_uploaded(): + response = requests.get(storage + latest_apk, stream=True) + response.raise_for_status() + file = BytesIO(response.content) + del response + requests.post('http://saucelabs.com/rest/v1/storage/' + + sauce_username + '/' + latest_apk + '?overwrite=true', + auth=(sauce_username, sauce_access_key), + data=file, + headers={'Content-Type': 'application/octet-stream'}) diff --git a/test/appium/tests/preconditions.py b/test/appium/tests/preconditions.py index 2b7aaeb52c..b7c175578a 100644 --- a/test/appium/tests/preconditions.py +++ b/test/appium/tests/preconditions.py @@ -1,6 +1,6 @@ -def set_chat_for_users_from_scratch(*args): +def set_password_as_new_user(*args): for view in args: view.request_password_icon.click() view.type_message_edit_box.send_keys("qwerty1234") diff --git a/test/appium/tests/test_chats.py b/test/appium/tests/test_chats.py index f41a560a30..7b78bb2339 100644 --- a/test/appium/tests/test_chats.py +++ b/test/appium/tests/test_chats.py @@ -1,22 +1,22 @@ import pytest from tests.basetestcase import MultiplyDeviceTestCase -from tests.preconditions import set_chat_for_users_from_scratch +from tests.preconditions import set_password_as_new_user from views.home import HomeView @pytest.mark.sanity -class TestMultiplyDevices(MultiplyDeviceTestCase): +class TestChats(MultiplyDeviceTestCase): def test_private_chat(self): device_1, device_2 = HomeView(self.driver_1), HomeView(self.driver_2) - set_chat_for_users_from_scratch(device_1, device_2) + set_password_as_new_user(device_1, device_2) device_1.back_button.click() chats_d1 = device_1.get_chats() chats_d1.profile_button.click() profile_d1 = chats_d1.profile_icon.click() - key = profile_d1.public_key_text.get_key() + key = profile_d1.public_key_text.text device_2.back_button.click() chats_d2 = device_2.get_chats() diff --git a/test/appium/tests/test_group_chats.py b/test/appium/tests/test_group_chats.py new file mode 100644 index 0000000000..44fa358007 --- /dev/null +++ b/test/appium/tests/test_group_chats.py @@ -0,0 +1,49 @@ +import pytest +from tests.basetestcase import MultiplyDeviceTestCase +from tests.preconditions import set_password_as_new_user +from views.home import HomeView +from views.chats import UserNameText + + +@pytest.mark.sanity +class TestDev(MultiplyDeviceTestCase): + + def test_group_chat(self): + device_1, device_2 = HomeView(self.driver_1), \ + HomeView(self.driver_2) + + set_password_as_new_user(device_2, device_1) + + device_1.back_button.click() + chats_d1 = device_1.get_chats() + chats_d1.profile_button.click() + profile_d1 = chats_d1.profile_icon.click() + key = profile_d1.public_key_text.text + + device_2.back_button.click() + chats_d2 = device_2.get_chats() + chats_d2.plus_button.click() + chats_d2.add_new_contact.click() + chats_d2.public_key_edit_box.send_keys(key) + chats_d2.confirm() + chats_d2.confirm_public_key_button.click() + + user_d1_name = chats_d2.user_name_text.text + + device_2.back_button.click() + chats_d2.new_group_chat_button.click() + + profile_d1.back_button.click() + + user_contact = UserNameText.UserContactByName(self.driver_2, user_d1_name) + user_contact.scroll_to_element() + user_contact.click() + + chats_d2.next_button.click() + chats_d2.name_edit_box.send_keys('new_chat') + chats_d2.save_button.click() + + chats_d2.chat_message_input.send_keys('SOMETHING') + chats_d2.send_message_button.click() + + profile_d1.find_text('SOMETHING') diff --git a/test/appium/views/base_element.py b/test/appium/views/base_element.py index 519dfaee4a..c0478c2262 100644 --- a/test/appium/views/base_element.py +++ b/test/appium/views/base_element.py @@ -2,6 +2,7 @@ from selenium.webdriver.common.by import By 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 pytest @@ -32,16 +33,22 @@ class BaseElement(object): return None def find_element(self): - try: - return self.wait_for_element() - except (NoSuchElementException, TimeoutException): - pytest.fail("'%s' not found by %s '%s'" % ( - self.name, self.locator.by, self.locator.value), pytrace=False) + return self.driver.find_element(self.locator.by, self.locator.value) - def wait_for_element(self, seconds=30): + 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): + for x in range(10): + action = TouchAction(self.driver) + action.press(x=0, y=1000).move_to(x=200, y=-1000).release().perform() + try: + self.find_element() + break + except NoSuchElementException: + pass + class BaseEditBox(BaseElement): diff --git a/test/appium/views/base_view.py b/test/appium/views/base_view.py index 5fa6f8e486..82c8539e0f 100644 --- a/test/appium/views/base_view.py +++ b/test/appium/views/base_view.py @@ -18,9 +18,9 @@ class BaseViewObject(object): self.driver.keyevent(66) def find_text(self, text): - element = BaseElement(self.driver) + element = BaseButton(self.driver) element.locator = element.Locator.xpath_selector('//*[@text="' + text + '"]') - return element.wait_for_element(30) + return element.wait_for_element(100) def get_chats(self): from views.chats import ChatsViewObject diff --git a/test/appium/views/chats.py b/test/appium/views/chats.py index 103d7f9962..63b4f21f33 100644 --- a/test/appium/views/chats.py +++ b/test/appium/views/chats.py @@ -7,7 +7,8 @@ class ProfileButton(BaseButton): def __init__(self, driver): super(ProfileButton, self).__init__(driver) self.locator = self.Locator.xpath_selector( - "//android.support.v4.view.ViewPager//android.view.ViewGroup[1]/android.widget.ImageView") + "//android.support.v4.view.ViewPager//android.view.ViewGroup[2]/android.view.ViewGroup[1]/android.view.View" + ) class ProfileIcon(BaseButton): @@ -38,6 +39,44 @@ class AddNewContactButton(BaseButton): "//android.widget.TextView[@text='Add new contact']") +class NewGroupChatButton(BaseButton): + + def __init__(self, driver): + super(NewGroupChatButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//android.widget.TextView[@text='New group chat']") + + class AugurContact(BaseButton): + def __init__(self, driver): + super(NewGroupChatButton.AugurContact, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//android.widget.TextView[@text='Augur']") + + class JarradContact(BaseButton): + def __init__(self, driver): + super(NewGroupChatButton.JarradContact, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//android.widget.TextView[@text='Jarrad']") + + class NextButton(BaseButton): + def __init__(self, driver): + super(NewGroupChatButton.NextButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//android.widget.TextView[@text='NEXT']") + + class NameEditBox(BaseEditBox): + def __init__(self, driver): + super(NewGroupChatButton.NameEditBox, self).__init__(driver) + self.locator = \ + self.Locator.xpath_selector("//android.widget.EditText[@NAF='true']") + + class SaveButton(BaseButton): + def __init__(self, driver): + super(NewGroupChatButton.SaveButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector( + "//android.widget.TextView[@text='SAVE']") + + class PublicKeyEditBox(BaseEditBox): def __init__(self, driver): @@ -73,6 +112,20 @@ class SendMessageButton(BaseButton): "android.view.ViewGroup[2]//android.widget.ImageView") +class UserNameText(BaseText): + + def __init__(self, driver): + super(UserNameText, self).__init__(driver) + self.locator = \ + self.Locator.xpath_selector("//android.widget.ScrollView//android.widget.TextView") + + class UserContactByName(BaseButton): + + def __init__(self, driver, user_name): + super(UserNameText.UserContactByName, self).__init__(driver) + self.locator = self.Locator.xpath_selector('//*[@text="' + user_name + '"]') + + class ChatsViewObject(BaseViewObject): def __init__(self, driver): @@ -83,7 +136,17 @@ class ChatsViewObject(BaseViewObject): self.profile_icon = ProfileIcon(self.driver) self.plus_button = PlusButton(self.driver) self.add_new_contact = AddNewContactButton(self.driver) + self.public_key_edit_box = PublicKeyEditBox(self.driver) self.confirm_public_key_button = ConfirmPublicKeyButton(self.driver) + self.chat_message_input = ChatMessageInput(self.driver) self.send_message_button = SendMessageButton(self.driver) + self.user_name_text = UserNameText(self.driver) + + self.new_group_chat_button = NewGroupChatButton(self.driver) + self.augur_contact = NewGroupChatButton.AugurContact(self.driver) + self.jarrad_contact = NewGroupChatButton.JarradContact(self.driver) + self.next_button = NewGroupChatButton.NextButton(self.driver) + self.name_edit_box = NewGroupChatButton.NameEditBox(self.driver) + self.save_button = NewGroupChatButton.SaveButton(self.driver) diff --git a/test/appium/views/home.py b/test/appium/views/home.py index 554948b24a..e2a74b3d56 100644 --- a/test/appium/views/home.py +++ b/test/appium/views/home.py @@ -9,6 +9,13 @@ class ContinueButton(BaseButton): self.locator = self.Locator.xpath_selector("//*[@text='Continue']") +class OkButton(BaseButton): + + def __init__(self, driver): + super(OkButton, self).__init__(driver) + self.locator = self.Locator.xpath_selector("//*[@text='OK']") + + class TypeMessageEditBox(BaseEditBox): def __init__(self, driver): @@ -29,11 +36,13 @@ class HomeView(BaseViewObject): def __init__(self, driver): super(HomeView, self).__init__(driver) self.continue_button = ContinueButton(driver) + self.ok_button = OkButton(driver) - try: - self.continue_button.click() - except Exception: - pass + for i in self.ok_button, self.continue_button: + try: + i.click() + except (NoSuchElementException, TimeoutException): + pass self.type_message_edit_box = TypeMessageEditBox(driver) self.request_password_icon = RequestPasswordIcon(driver) diff --git a/test/appium/views/profile.py b/test/appium/views/profile.py index 7b12840a60..bfc29dc356 100644 --- a/test/appium/views/profile.py +++ b/test/appium/views/profile.py @@ -6,14 +6,7 @@ class PublicKeyText(BaseText): def __init__(self, driver): super(PublicKeyText, self).__init__(driver) - self.locator = self.Locator.xpath_selector("//android.widget.TextView") - - def get_key(self): - texts = self.driver.find_elements(self.locator.by, self.locator.value) - for i in texts: - if i.text.startswith('0x04'): - return i.text - pytest.fail("Public key wasn't found!") + self.locator = self.Locator.xpath_selector("//android.widget.TextView[@instance='7']") class ProfileViewObject(BaseViewObject):