e2e: LambdaTest trial

This commit is contained in:
Yevheniia Berdnyk 2024-10-24 01:15:05 +03:00
parent 9ced0e4131
commit 735ab61d4a
8 changed files with 159 additions and 90 deletions

View File

@ -115,6 +115,11 @@ pipeline {
usernameVariable: 'SAUCE_USERNAME',
passwordVariable: 'SAUCE_ACCESS_KEY'
),
usernamePassword(
credentialsId: 'lambda-test-api',
usernameVariable: 'LAMBDA_TEST_USERNAME',
passwordVariable: 'LAMBDA_TEST_ACCESS_KEY'
),
string(
credentialsId: 'etherscan-api-key',
variable: 'ETHERSCAN_API_KEY'

View File

@ -0,0 +1,28 @@
import requests
from conftest import lambda_test_username, lambda_test_access_key
from tests import test_suite_data
session = requests.Session()
session.auth = (lambda_test_username, lambda_test_access_key)
def upload_apk(apk_file_path):
resp = session.post(
# url="https://manual-api.lambdatest.com/app/upload/realDevice",
url="https://manual-api.lambdatest.com/app/upload/virtualDevice",
files={'appFile': open(apk_file_path, 'rb')},
data={'name': test_suite_data.apk_name}
)
assert resp.status_code == 200
return resp.json()['app_url']
def update_session(session_id, session_name, status):
resp = session.get(
url="https://mobile-api.lambdatest.com/mobile-automation/api/v1/sessions/%s" % session_id,
data={
"name": session_name #, "status_ind": status
}
)
assert resp.status_code == 200

View File

@ -19,9 +19,11 @@ from urllib3.exceptions import MaxRetryError, ProtocolError
from support.api.network_api import NetworkApi
from tests import test_suite_data, start_threads, appium_container, pytest_config_global, transl
from tests.conftest import sauce_username, sauce_access_key, apibase, github_report, run_name
from tests.conftest import sauce_username, sauce_access_key, apibase, github_report, run_name, option, \
lambda_test_username, lambda_test_access_key
executor_sauce_lab = 'https://%s:%s@ondemand.%s:443/wd/hub' % (sauce_username, sauce_access_key, apibase)
executor_lambda_test = 'https://%s:%s@mobile-hub.lambdatest.com/wd/hub' % (lambda_test_username, lambda_test_access_key)
executor_local = 'http://localhost:4723/wd/hub'
@ -88,7 +90,6 @@ def get_capabilities_sauce_lab():
caps['sauce:options']['name'] = test_suite_data.current_test.name
caps['sauce:options']['maxDuration'] = 3600
caps['sauce:options']['idleTimeout'] = 1000
# caps['sauce:options']['android.gpu.mode'] = 'hardware'
options = AppiumOptions()
options.load_capabilities(caps)
@ -96,10 +97,50 @@ def get_capabilities_sauce_lab():
return options
# def update_capabilities_sauce_lab(new_capabilities: dict):
# caps = get_capabilities_sauce_lab().copy()
# caps.update(new_capabilities)
# return caps
def get_lambda_test_capabilities_real_device():
capabilities = {
"lt:options": {
"w3c": True,
"platformName": "android",
"deviceName": "Pixel 8",
"platformVersion": "14",
"app": "lt://APP10160471311729636675434695", # lambda_test_apk_url,
"devicelog": True,
"visual": True,
# "network": True,
"video": True,
"build": run_name,
"name": test_suite_data.current_test.group_name,
"idleTimeout": 1000,
"isRealMobile": True
}
}
options = AppiumOptions()
options.load_capabilities(capabilities)
return options
def get_lambda_test_capabilities_emulator():
capabilities = {
"lt:options": {
"w3c": True,
"platformName": "android",
"deviceName": "Pixel 6",
"appiumVersion": "2.11.0",
"platformVersion": "14",
"app": "lt://APP10160522181729681886587724", #option.lambda_test_apk_url,
"devicelog": True,
"visual": True,
# "network": True,
"video": True,
"build": run_name,
"name": test_suite_data.current_test.group_name,
"idleTimeout": 1000
}
}
options = AppiumOptions()
options.load_capabilities(capabilities)
return options
def get_app_path():
@ -305,14 +346,15 @@ def create_shared_drivers(quantity):
else:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
print('SC Executor: %s' % executor_sauce_lab)
print('LT Executor: %s' % executor_lambda_test)
try:
drivers = loop.run_until_complete(start_threads(test_suite_data.current_test.name,
quantity,
Driver,
drivers,
command_executor=executor_sauce_lab,
options=get_capabilities_sauce_lab()))
command_executor=executor_lambda_test,
options=get_lambda_test_capabilities_emulator()))
# options=get_lambda_test_capabilities_real_device()))
if len(drivers) < quantity:
test_suite_data.current_test.testruns[-1].error = "Not all %s drivers are created" % quantity
@ -368,8 +410,8 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
def setup_method(self, method):
if not self.drivers:
pytest.fail(test_suite_data.current_test.testruns[-1].error)
for _, driver in self.drivers.items():
driver.execute_script("sauce:context=Started %s" % method.__name__)
# for _, driver in self.drivers.items():
# driver.execute_script("sauce:context=Started %s" % method.__name__)
jobs = test_suite_data.current_test.testruns[-1].jobs
if not jobs:
for index, driver in self.drivers.items():
@ -400,6 +442,7 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
@pytest.fixture(scope='class', autouse=True)
def prepare(self, request):
test_suite_data.current_test.group_name = request.cls.__name__
try:
request.cls.prepare_devices(request)
finally:
@ -410,7 +453,7 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
def teardown_class(cls):
from tests.conftest import sauce
requests_session = requests.Session()
requests_session.auth = (sauce_username, sauce_access_key)
requests_session.auth = (lambda_test_username, lambda_test_access_key)
if test_suite_data.tests[0].testruns[-1].error and 'setup failed' in test_suite_data.tests[0].testruns[
-1].error:
group_setup_failed = True
@ -425,28 +468,34 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
log_contents.append(pull_requests_log(driver=driver))
log_names.append('%s_requests%s.log' % (cls.__name__, i))
session_id = driver.session_id
from support.lambda_test import update_session
try:
sauce.jobs.update_job(username=sauce_username, job_id=session_id, name=cls.__name__)
# sauce.jobs.update_job(username=sauce_username, job_id=session_id, name=cls.__name__)
update_session(
session_id=session_id,
session_name=cls.__name__,
status="failed" if group_setup_failed else "passed"
)
except (RemoteDisconnected, SauceException, requests.exceptions.ConnectionError):
pass
try:
driver.quit()
except WebDriverException:
pass
url = 'https://api.%s/rest/v1/%s/jobs/%s/assets/%s' % (apibase, sauce_username, session_id, "log.json")
try:
WebDriverWait(driver, 60, 2).until(lambda _: requests_session.get(url).status_code == 200)
commands = requests_session.get(url).json()
for command in commands:
try:
if command['message'].startswith("Started "):
for test in test_suite_data.tests:
if command['message'] == "Started %s" % test.name:
test.testruns[-1].first_commands[session_id] = commands.index(command) + 1
except KeyError:
continue
except (RemoteDisconnected, requests.exceptions.ConnectionError, TimeoutException):
pass
# url = 'https://api.%s/rest/v1/%s/jobs/%s/assets/%s' % (apibase, sauce_username, session_id, "log.json")
# try:
# WebDriverWait(driver, 60, 2).until(lambda _: requests_session.get(url).status_code == 200)
# commands = requests_session.get(url).json()
# for command in commands:
# try:
# if command['message'].startswith("Started "):
# for test in test_suite_data.tests:
# if command['message'] == "Started %s" % test.name:
# test.testruns[-1].first_commands[session_id] = commands.index(command) + 1
# except KeyError:
# continue
# except (RemoteDisconnected, requests.exceptions.ConnectionError, TimeoutException):
# pass
except AttributeError:
pass
finally:

View File

@ -20,6 +20,10 @@ from tests import test_suite_data, appium_container
sauce_username = environ.get('SAUCE_USERNAME')
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
lambda_test_username = environ.get('LAMBDA_TEST_USERNAME')
lambda_test_access_key = environ.get('LAMBDA_TEST_ACCESS_KEY')
github_token = environ.get('GIT_HUB_TOKEN')
@ -141,6 +145,7 @@ def pytest_addoption(parser):
@dataclass
class Option:
datacenter: str = None
lambda_test_apk_url: str = None
option = Option()
@ -151,15 +156,19 @@ sauce = None
run_name = None
# lambda_test_apk_url = None
def is_master(config):
return not hasattr(config, 'workerinput')
def is_uploaded():
stored_files = sauce.storage.files()
for i in range(len(stored_files)):
if stored_files[i].name == test_suite_data.apk_name:
return True
return False # ToDo: add verification
# stored_files = sauce.storage.files()
# for i in range(len(stored_files)):
# if stored_files[i].name == test_suite_data.apk_name:
# return True
@contextmanager
@ -182,21 +191,22 @@ class UploadApkException(Exception):
def _upload_and_check_response(apk_file_path):
from support.lambda_test import upload_apk
with _upload_time_limit(1000):
resp = sauce.storage.upload(apk_file_path)
try:
if resp.name != test_suite_data.apk_name:
raise UploadApkException("Incorrect apk was uploaded to Sauce storage, response:\n%s" % resp)
except AttributeError:
raise UploadApkException("Error when uploading apk to Sauce storage, response:\n%s" % resp)
# # resp = sauce.storage.upload(apk_file_path)
return upload_apk(apk_file_path)
# try:
# if resp.name != test_suite_data.apk_name:
# raise UploadApkException("Incorrect apk was uploaded to Sauce storage, response:\n%s" % resp)
# except AttributeError:
# raise UploadApkException("Error when uploading apk to Sauce storage, response:\n%s" % resp)
def _upload_and_check_response_with_retries(apk_file_path, retries=3):
for _ in range(retries):
try:
_upload_and_check_response(apk_file_path)
break
return _upload_and_check_response(apk_file_path)
# break
except (ConnectionError, RemoteDisconnected, c_er):
time.sleep(10)
@ -254,8 +264,8 @@ def pytest_configure(config):
else:
raise NotImplementedError("Unknown SauceLabs datacenter")
global sauce
sauce = SauceLab('https://api.' + apibase + '/', sauce_username, sauce_access_key)
# global sauce
# sauce = SauceLab('https://api.' + apibase + '/', sauce_username, sauce_access_key)
if config.getoption('log_steps'):
import logging
logging.basicConfig(level=logging.INFO)
@ -271,6 +281,18 @@ def pytest_configure(config):
else:
run_name = get_run_name(config, new_one=False)
if is_master(config):
apk_src = config.getoption('apk')
if apk_src.startswith('http'):
apk_path = _download_apk(apk_src)
else:
apk_path = apk_src
# global lambda_test_apk_url
option.lambda_test_apk_url = _upload_and_check_response(apk_path)
if apk_src.startswith('http'):
os.remove(apk_path)
if not is_master(config):
return
@ -285,16 +307,9 @@ def pytest_configure(config):
description='e2e tests are running'
)
if config.getoption('env') == 'sauce' and not is_uploaded():
apk_src = config.getoption('apk')
if apk_src.startswith('http'):
apk_path = _download_apk(apk_src)
else:
apk_path = apk_src
_upload_and_check_response_with_retries(apk_path)
if apk_src.startswith('http'):
os.remove(apk_path)
def pytest_configure_node(node):
node.workerinput['lambda_test_apk_url'] = node.config.option.lambda_test_apk_url
def pytest_unconfigure(config):
@ -366,10 +381,10 @@ def pytest_runtest_makereport(item, call):
test_suite_data.current_test.group_name = item.instance.__class__.__name__
error = catch_error()
final_error = '%s %s' % (error_intro, error)
if is_sauce_env:
update_sauce_jobs(test_suite_data.current_test.group_name,
test_suite_data.current_test.testruns[-1].jobs,
report.passed)
# if is_sauce_env:
# update_sauce_jobs(test_suite_data.current_test.group_name,
# test_suite_data.current_test.testruns[-1].jobs,
# report.passed)
if error:
test_suite_data.current_test.testruns[-1].error = final_error
github_report.save_test(test_suite_data.current_test)
@ -386,8 +401,8 @@ def pytest_runtest_makereport(item, call):
current_test.testruns[-1].run = False
if error:
current_test.testruns[-1].error = '%s [[%s]]' % (error, report.wasxfail)
if is_sauce_env:
update_sauce_jobs(current_test.name, current_test.testruns[-1].jobs, report.passed)
# if is_sauce_env:
# update_sauce_jobs(current_test.name, current_test.testruns[-1].jobs, report.passed)
if item.config.getoption('docker'):
device_stats = appium_container.get_device_stats()
if item.config.getoption('bugreport'):
@ -450,35 +465,3 @@ def pytest_runtest_protocol(item, nextitem):
break # rerun
else:
return True # no need to rerun
# @pytest.fixture(scope="session", autouse=False)
# def faucet_for_senders():
# network_api = NetworkApi()
# for user in transaction_senders.values():
# network_api.faucet(address=user['address'])
@pytest.fixture
def messages_number(request):
return int(request.config.getoption('messages_number'))
@pytest.fixture
def message_wait_time(request):
return int(request.config.getoption('message_wait_time'))
@pytest.fixture
def participants_number(request):
return int(request.config.getoption('participants_number'))
@pytest.fixture
def chat_name(request):
return request.config.getoption('chat_name')
@pytest.fixture
def user_public_key(request):
return request.config.getoption('user_public_key')

View File

@ -14,6 +14,7 @@ from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="new_one_2")
@marks.nightly
@marks.lt
class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
def prepare_devices(self):

View File

@ -13,6 +13,7 @@ from views.sign_in_view import SignInView
@pytest.mark.xdist_group(name="new_one_3")
@marks.nightly
@marks.lt
class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
def prepare_devices(self):

View File

@ -13,3 +13,5 @@ upgrade = pytest.mark.upgrade
skip = pytest.mark.skip
xfail = pytest.mark.xfail
secured = pytest.mark.secured
lt = pytest.mark.lt # temp

View File

@ -410,7 +410,7 @@ class BaseView(object):
def just_fyi(self, some_str):
self.driver.info('# STEP: %s' % some_str, device=False)
self.driver.execute_script("sauce:context=STEP: %s" % some_str)
# self.driver.execute_script("sauce:context=STEP: %s" % some_str)
def hide_keyboard_if_shown(self):
if self.driver.is_keyboard_shown():