e2e: setup and teardown with shared drivers

This commit is contained in:
yevh-berdnyk 2021-12-15 21:14:16 +02:00
parent 2c23ab4c5d
commit b18d7b6a2a
No known key found for this signature in database
GPG Key ID: 0642C73C66214825
5 changed files with 207 additions and 117 deletions

View File

@ -1,6 +1,6 @@
[pytest] [pytest]
norecursedirs = .git views norecursedirs = .git views
addopts = -s -v --tb=line --junitxml=result.xml addopts = -s -v --tb=line --junitxml=result.xml --dist loadgroup -n 5
junit_family=legacy junit_family=legacy
markers = markers =
critical: TCs by priority critical: TCs by priority

View File

@ -30,9 +30,9 @@ pycryptodome==3.9.9
pyethash==0.1.27 pyethash==0.1.27
pyparsing==2.4.7 pyparsing==2.4.7
pysha3==1.0.2 pysha3==1.0.2
pytest==6.1.2 pytest==6.2.0
pytest-forked==1.3.0 pytest-forked==1.3.0
pytest-xdist==2.2.0 pytest-xdist==2.5.0
python-dateutil==2.8.1 python-dateutil==2.8.1
pytz==2020.4 pytz==2020.4
PyYAML==5.4 PyYAML==5.4

View File

@ -1,26 +1,35 @@
import emoji
import random import random
from dateutil import parser
from tests import marks
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase
from views.sign_in_view import SignInView
from datetime import timedelta from datetime import timedelta
from time import sleep from time import sleep
import emoji
import pytest
from dateutil import parser
class TestPublicChatMultipleDevice(MultipleDeviceTestCase): from tests import marks
from tests.base_test_case import MultipleDeviceTestCase, SingleDeviceTestCase, create_shared_drivers, \
MultipleSharedDeviceTestCase
from views.home_view import HomeView
from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="public_chat")
class TestPublicChatMultipleDeviceMerged(MultipleSharedDeviceTestCase):
@classmethod
def setup_class(cls):
cls.drivers, cls.loop = create_shared_drivers(2)
device_1, device_2 = SignInView(cls.drivers[0]), SignInView(cls.drivers[1])
home_1, home_2 = device_1.create_user(), device_2.create_user()
profile_1 = home_1.profile_button.click()
cls.username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
home_2.home_button.click()
@marks.testrail_id(5313) @marks.testrail_id(5313)
@marks.critical @marks.critical
def test_public_chat_messaging_emojis_timestamps(self): def test_public_chat_messaging_emojis_timestamps(self):
self.create_drivers(2) home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
home_1, home_2 = device_1.create_user(), device_2.create_user()
profile_1 = home_1.profile_button.click()
default_username_1 = profile_1.default_username_text.text
profile_1.home_button.click()
home_2.home_button.click()
home_1.just_fyi("Check preselected chats, redirect to status chat") home_1.just_fyi("Check preselected chats, redirect to status chat")
home_1.plus_button.click_until_presence_of_element(home_1.join_public_chat_button) home_1.plus_button.click_until_presence_of_element(home_1.join_public_chat_button)
home_1.join_public_chat_button.click() home_1.join_public_chat_button.click()
@ -50,8 +59,8 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
if timestamp not in sent_time_variants: if timestamp not in sent_time_variants:
self.errors.append( self.errors.append(
"Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(','), timestamp)) "Timestamp is not shown, expected '%s', in fact '%s'" % (sent_time_variants.join(','), timestamp))
if chat_2.chat_element_by_text(message).username.text != default_username_1: if chat_2.chat_element_by_text(message).username.text != self.username_1:
self.errors.append("Default username '%s' is not shown next to the received message" % default_username_1) self.errors.append("Default username '%s' is not shown next to the received message" % self.username_1)
chat_1.send_message(emoji_message) chat_1.send_message(emoji_message)
for chat in chat_1, chat_2: for chat in chat_1, chat_2:
@ -63,13 +72,11 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(5360) @marks.testrail_id(5360)
@marks.critical @marks.critical
def test_unread_messages_counter_public_chat(self): def test_unread_messages_counter_public_chat(self):
self.create_drivers(2) home_1, home_2 = HomeView(self.drivers[0]), HomeView(self.drivers[1])
driver_2 = self.drivers[1] home_1.get_back_to_home_view()
home_1, home_2 = SignInView(self.drivers[0]).create_user(), SignInView(self.drivers[1]).create_user() home_2.get_back_to_home_view()
profile_1 = home_1.profile_button.click() home_1.home_button.click()
username_1 = profile_1.default_username_text.text home_2.home_button.click()
profile_1.home_button.click()
chat_name = home_1.get_random_chat_name() chat_name = home_1.get_random_chat_name()
chat_1, chat_2 = home_1.join_public_chat(chat_name), home_2.join_public_chat(chat_name) chat_1, chat_2 = home_1.join_public_chat(chat_name), home_2.join_public_chat(chat_name)
chat_1.send_message('пиу') chat_1.send_message('пиу')
@ -87,7 +94,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
home_1.just_fyi("Check unread message counter when mentioned in public chat") home_1.just_fyi("Check unread message counter when mentioned in public chat")
chat_2 = home_2.get_chat_view() chat_2 = home_2.get_chat_view()
chat_2.select_mention_from_suggestion_list(username_1, username_1[:2]) chat_2.select_mention_from_suggestion_list(self.username_1, self.username_1[:2])
chat_2.send_message_button.click() chat_2.send_message_button.click()
chat_element.new_messages_counter.wait_for_element(30) chat_element.new_messages_counter.wait_for_element(30)
chat_element.new_messages_counter.wait_for_element_text("1", 60) chat_element.new_messages_counter.wait_for_element_text("1", 60)
@ -104,6 +111,7 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
chat_2.chat_element_by_text(message_2).wait_for_element(20) chat_2.chat_element_by_text(message_2).wait_for_element(20)
home_2.just_fyi("Check that unread message indicator is not reappeared after relogin") home_2.just_fyi("Check that unread message indicator is not reappeared after relogin")
driver_2 = self.drivers[1]
driver_2.close_app() driver_2.close_app()
driver_2.launch_app() driver_2.launch_app()
SignInView(driver_2).sign_in() SignInView(driver_2).sign_in()
@ -112,6 +120,9 @@ class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
self.errors.append('New messages counter is shown after relogin') self.errors.append('New messages counter is shown after relogin')
self.errors.verify_no_errors() self.errors.verify_no_errors()
class TestPublicChatMultipleDevice(MultipleDeviceTestCase):
@marks.testrail_id(6270) @marks.testrail_id(6270)
@marks.medium @marks.medium
def test_mark_all_messages_as_read_public_chat(self): def test_mark_all_messages_as_read_public_chat(self):

View File

@ -19,47 +19,89 @@ from tests import test_suite_data, start_threads, appium_container, pytest_confi
import base64 import base64
from re import findall from re import findall
sauce_username = environ.get('SAUCE_USERNAME')
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
executor_sauce_lab = 'http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (sauce_username, sauce_access_key)
executor_local = 'http://localhost:4723/wd/hub'
implicit_wait = 5
def get_capabilities_local():
desired_caps = dict()
if pytest_config_global['docker']:
# apk is in shared volume directory
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
else:
apk = pytest_config_global['apk']
desired_caps['app'] = apk
desired_caps['deviceName'] = 'nexus_5'
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.9.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['newCommandTimeout'] = 600
desired_caps['fullReset'] = False
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
return desired_caps
def add_local_devices_to_capabilities():
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+)|\bemulator-\d+)", line)
if serial:
capabilities = get_capabilities_local()
capabilities['udid'] = serial[0][0]
updated_capabilities.append(capabilities)
return updated_capabilities
def get_capabilities_sauce_lab():
desired_caps = dict()
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
desired_caps['build'] = pytest_config_global['build']
desired_caps['name'] = test_suite_data.current_test.name
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.18.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 600
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
desired_caps['ignoreUnimportantViews'] = False
desired_caps['enableNotificationListener'] = True
desired_caps['maxDuration'] = 1800
return desired_caps
def update_capabilities_sauce_lab(new_capabilities: dict):
caps = get_capabilities_sauce_lab().copy()
caps.update(new_capabilities)
return caps
class AbstractTestCase: class AbstractTestCase:
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
@property def print_sauce_lab_info(self, driver):
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)
@property
def executor_local(self):
return 'http://localhost:4723/wd/hub'
@staticmethod
def print_sauce_lab_info(driver):
sys.stdout = sys.stderr sys.stdout = sys.stderr
print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id, print("SauceOnDemandSessionID=%s job-name=%s" % (driver.session_id,
pytest_config_global['build'])) pytest_config_global['build']))
@staticmethod def get_translation_by_key(self, key):
def get_translation_by_key(key):
return transl[key] return transl[key]
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+)|\bemulator-\d+)", line)
if serial:
capabilities = self.capabilities_local
capabilities['udid'] = serial[0][0]
updated_capabilities.append(capabilities)
return updated_capabilities
@property @property
def app_path(self): def app_path(self):
app_path = '/storage/emulated/0/Android/data/im.status.ethereum.pr/files/Download/' if findall(r'pr\d\d\d\d\d', app_path = '/storage/emulated/0/Android/data/im.status.ethereum.pr/files/Download/' if findall(r'pr\d\d\d\d\d',
@ -71,53 +113,6 @@ class AbstractTestCase:
def geth_path(self): def geth_path(self):
return self.app_path + 'geth.log' return self.app_path + 'geth.log'
@property
def capabilities_sauce_lab(self):
desired_caps = dict()
desired_caps['app'] = 'sauce-storage:' + test_suite_data.apk_name
desired_caps['build'] = pytest_config_global['build']
desired_caps['name'] = test_suite_data.current_test.name
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.18.1'
desired_caps['platformVersion'] = '10.0'
desired_caps['deviceName'] = 'Android GoogleAPI Emulator'
desired_caps['deviceOrientation'] = "portrait"
desired_caps['commandTimeout'] = 600
desired_caps['idleTimeout'] = 600
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
desired_caps['ignoreUnimportantViews'] = False
desired_caps['enableNotificationListener'] = True
desired_caps['maxDuration'] = 1800
return desired_caps
def update_capabilities_sauce_lab(self, new_capabilities: dict):
caps = self.capabilities_sauce_lab.copy()
caps.update(new_capabilities)
return caps
@property
def capabilities_local(self):
desired_caps = dict()
if pytest_config_global['docker']:
# apk is in shared volume directory
apk = '/root/shared_volume/%s' % pytest_config_global['apk']
else:
apk = pytest_config_global['apk']
desired_caps['app'] = apk
desired_caps['deviceName'] = 'nexus_5'
desired_caps['platformName'] = 'Android'
desired_caps['appiumVersion'] = '1.9.1'
desired_caps['platformVersion'] = pytest_config_global['platform_version']
desired_caps['newCommandTimeout'] = 600
desired_caps['fullReset'] = False
desired_caps['unicodeKeyboard'] = True
desired_caps['automationName'] = 'UiAutomator2'
desired_caps['setWebContentDebuggingEnabled'] = True
return desired_caps
@abstractmethod @abstractmethod
def setup_method(self, method): def setup_method(self, method):
raise NotImplementedError('Should be overridden from a child class') raise NotImplementedError('Should be overridden from a child class')
@ -130,10 +125,6 @@ class AbstractTestCase:
def environment(self): def environment(self):
return pytest_config_global['env'] return pytest_config_global['env']
@property
def implicitly_wait(self):
return 5
network_api = NetworkApi() network_api = NetworkApi()
github_report = GithubHtmlReport() github_report = GithubHtmlReport()
@ -197,13 +188,13 @@ class SingleDeviceTestCase(AbstractTestCase):
appium_container.start_appium_container(pytest_config_global['docker_shared_volume']) appium_container.start_appium_container(pytest_config_global['docker_shared_volume'])
appium_container.connect_device(pytest_config_global['device_ip']) appium_container.connect_device(pytest_config_global['device_ip'])
(executor, capabilities) = (self.executor_sauce_lab, self.capabilities_sauce_lab) if \ (executor, capabilities) = (executor_sauce_lab, get_capabilities_sauce_lab()) if \
self.environment == 'sauce' else (self.executor_local, self.capabilities_local) self.environment == 'sauce' else (executor_local, get_capabilities_local())
for key, value in kwargs.items(): for key, value in kwargs.items():
capabilities[key] = value capabilities[key] = value
self.driver = Driver(executor, capabilities) self.driver = Driver(executor, capabilities)
test_suite_data.current_test.testruns[-1].jobs[self.driver.session_id] = 1 test_suite_data.current_test.testruns[-1].jobs[self.driver.session_id] = 1
self.driver.implicitly_wait(self.implicitly_wait) self.driver.implicitly_wait(implicit_wait)
self.errors = Errors() self.errors = Errors()
if pytest_config_global['docker']: if pytest_config_global['docker']:
@ -241,7 +232,7 @@ class LocalMultipleDeviceTestCase(AbstractTestCase):
def teardown_method(self, method): def teardown_method(self, method):
for driver in self.drivers: for driver in self.drivers:
try: try:
self.add_alert_text_to_report(driver) self.add_alert_text_to_report(self.drivers[driver])
self.drivers[driver].quit() self.drivers[driver].quit()
except WebDriverException: except WebDriverException:
pass pass
@ -263,12 +254,12 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
self.drivers = self.loop.run_until_complete(start_threads(quantity, self.drivers = self.loop.run_until_complete(start_threads(quantity,
Driver, Driver,
self.drivers, self.drivers,
self.executor_sauce_lab, executor_sauce_lab,
self.update_capabilities_sauce_lab(capabilities))) update_capabilities_sauce_lab(capabilities)))
for driver in range(quantity): for driver in range(quantity):
test_suite_data.current_test.testruns[-1].jobs[self.drivers[driver].session_id] = driver + 1 test_suite_data.current_test.testruns[-1].jobs[self.drivers[driver].session_id] = driver + 1
self.drivers[driver].implicitly_wait( self.drivers[driver].implicitly_wait(
custom_implicitly_wait if custom_implicitly_wait else self.implicitly_wait) custom_implicitly_wait if custom_implicitly_wait else implicit_wait)
def teardown_method(self, method): def teardown_method(self, method):
geth_names, geth_contents = [], [] geth_names, geth_contents = [], []
@ -290,10 +281,97 @@ class SauceMultipleDeviceTestCase(AbstractTestCase):
cls.loop.close() cls.loop.close()
def create_shared_drivers(quantity):
drivers = dict()
if pytest_config_global['env'] == 'local':
capabilities = add_local_devices_to_capabilities()
for i in range(quantity):
driver = Driver(executor_local, capabilities[i])
test_suite_data.current_test.testruns[-1].jobs[driver.session_id] = i + 1
driver.implicitly_wait(implicit_wait)
drivers[i] = driver
loop = None
else:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
capabilities = {'maxDuration': 1800}
drivers = loop.run_until_complete(start_threads(quantity,
Driver,
drivers,
executor_sauce_lab,
update_capabilities_sauce_lab(capabilities)))
for i in range(quantity):
test_suite_data.current_test.testruns[-1].jobs[drivers[i].session_id] = i + 1
drivers[i].implicitly_wait(implicit_wait)
return drivers, loop
class LocalSharedMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
jobs = test_suite_data.current_test.testruns[-1].jobs
if not jobs:
for index, driver in self.drivers.items():
jobs[driver.session_id] = index + 1
self.errors = Errors()
def teardown_method(self, method):
for driver in self.drivers:
try:
self.add_alert_text_to_report(self.drivers[driver])
except WebDriverException:
pass
@classmethod
def teardown_class(cls):
for driver in cls.drivers:
try:
cls.drivers[driver].quit()
except WebDriverException:
pass
class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
jobs = test_suite_data.current_test.testruns[-1].jobs
if not jobs:
for index, driver in self.drivers.items():
jobs[driver.session_id] = index + 1
self.errors = Errors()
def teardown_method(self, method):
geth_names, geth_contents = [], []
for driver in self.drivers:
try:
self.print_sauce_lab_info(self.drivers[driver])
self.add_alert_text_to_report(self.drivers[driver])
geth_names.append(
'%s_geth%s.log' % (test_suite_data.current_test.name, str(self.drivers[driver].number)))
geth_contents.append(self.pull_geth(self.drivers[driver]))
except (WebDriverException, AttributeError):
pass
finally:
geth = {geth_names[i]: geth_contents[i] for i in range(len(geth_names))}
self.github_report.save_test(test_suite_data.current_test, geth)
@classmethod
def teardown_class(cls):
for driver in cls.drivers:
try:
cls.drivers[driver].quit()
except WebDriverException:
pass
cls.loop.close()
if pytest_config_global['env'] == 'local': if pytest_config_global['env'] == 'local':
MultipleDeviceTestCase = LocalMultipleDeviceTestCase MultipleDeviceTestCase = LocalMultipleDeviceTestCase
MultipleSharedDeviceTestCase = LocalSharedMultipleDeviceTestCase
else: else:
MultipleDeviceTestCase = SauceMultipleDeviceTestCase MultipleDeviceTestCase = SauceMultipleDeviceTestCase
MultipleSharedDeviceTestCase = SauceSharedMultipleDeviceTestCase
class NoDeviceTestCase(AbstractTestCase): class NoDeviceTestCase(AbstractTestCase):

View File

@ -282,7 +282,8 @@ def pytest_runtest_protocol(item, nextitem):
for i in range(rerun_count): for i in range(rerun_count):
reports = runtestprotocol(item, nextitem=nextitem) reports = runtestprotocol(item, nextitem=nextitem)
for report in reports: for report in reports:
if report.failed and should_rerun_test(report.longreprtext): is_in_group = [i for i in item.iter_markers(name='xdist_group')]
if report.failed and should_rerun_test(report.longreprtext) and not is_in_group:
break # rerun break # rerun
else: else:
return True # no need to rerun return True # no need to rerun