testrail report for extended automated tests

Signed-off-by: yevh-berdnyk <ie.berdnyk@gmail.com>
This commit is contained in:
Anton Danchenko 2018-04-26 15:34:15 +03:00 committed by yevh-berdnyk
parent 291932db08
commit 942c703bd8
No known key found for this signature in database
GPG Key ID: E9B425FDFC4DEA9C
10 changed files with 228 additions and 108 deletions

View File

@ -0,0 +1,63 @@
import json
import hmac
import os
from hashlib import md5
from tests import SingleTestData
class BaseTestReport:
TEST_REPORT_DIR = "%s/../report" % os.path.dirname(os.path.abspath(__file__))
def __init__(self, sauce_username, sauce_access_key):
self.sauce_username = sauce_username
self.sauce_access_key = sauce_access_key
self.init_report()
def init_report(self):
if not os.path.exists(self.TEST_REPORT_DIR):
os.makedirs(self.TEST_REPORT_DIR)
# delete all old files in report dir
file_list = [f for f in os.listdir(self.TEST_REPORT_DIR)]
for f in file_list:
os.remove(os.path.join(self.TEST_REPORT_DIR, f))
def get_test_report_file_path(self, test_name):
file_name = "%s.json" % test_name
return os.path.join(self.TEST_REPORT_DIR, file_name)
def save_test(self, test):
file_path = self.get_test_report_file_path(test.name)
json.dump(test.__dict__, open(file_path, 'w'))
def get_all_tests(self):
tests = list()
file_list = [f for f in os.listdir(self.TEST_REPORT_DIR)]
for file_name in file_list:
file_path = os.path.join(self.TEST_REPORT_DIR, file_name)
test_dict = json.load(open(file_path))
tests.append(SingleTestData(name=test_dict['name'], steps=test_dict['steps'],
jobs=test_dict['jobs'], error=test_dict['error'],
testrail_case_id=test_dict['testrail_case_id']))
return tests
def get_failed_tests(self):
tests = self.get_all_tests()
failed = list()
for test in tests:
if test.error is not None:
failed.append(test)
return failed
def get_passed_tests(self):
tests = self.get_all_tests()
passed = list()
for test in tests:
if test.error is None:
passed.append(test)
return passed
def get_sauce_job_url(self, job_id):
token = hmac.new(bytes(self.sauce_username + ":" + self.sauce_access_key, 'latin-1'),
bytes(job_id, 'latin-1'), md5).hexdigest()
return "https://saucelabs.com/jobs/%s?auth=%s" % (job_id, token)

View File

@ -1,31 +1,13 @@
import json
from hashlib import md5
import hmac
import os
from tests import SingleTestData
from support.base_test_report import BaseTestReport
class GithubHtmlReport:
class GithubHtmlReport(BaseTestReport):
TEST_REPORT_DIR = "%s/../report" % os.path.dirname(os.path.abspath(__file__))
def __init__(self, sauce_username, sauce_access_key):
self.sauce_username = sauce_username
self.sauce_access_key = sauce_access_key
self.init_report()
def init_report(self):
if not os.path.exists(self.TEST_REPORT_DIR):
os.makedirs(self.TEST_REPORT_DIR)
# delete all old files in report dir
file_list = [f for f in os.listdir(self.TEST_REPORT_DIR)]
for f in file_list:
os.remove(os.path.join(self.TEST_REPORT_DIR, f))
def get_test_report_file_path(self, test_name):
file_name = "%s.json" % test_name
return os.path.join(self.TEST_REPORT_DIR, file_name)
super(GithubHtmlReport, self).__init__(sauce_username, sauce_access_key)
def build_html_report(self):
tests = self.get_all_tests()
@ -49,36 +31,6 @@ class GithubHtmlReport:
else:
return None
def save_test(self, test):
file_path = self.get_test_report_file_path(test.name)
json.dump(test.__dict__, open(file_path, 'w'))
def get_all_tests(self):
tests = list()
file_list = [f for f in os.listdir(self.TEST_REPORT_DIR)]
for file_name in file_list:
file_path = os.path.join(self.TEST_REPORT_DIR, file_name)
test_dict = json.load(open(file_path))
tests.append(SingleTestData(name=test_dict['name'], steps=test_dict['steps'],
jobs=test_dict['jobs'], error=test_dict['error']))
return tests
def get_failed_tests(self):
tests = self.get_all_tests()
failed = list()
for test in tests:
if test.error is not None:
failed.append(test)
return failed
def get_passed_tests(self):
tests = self.get_all_tests()
passed = list()
for test in tests:
if test.error is None:
passed.append(test)
return passed
def build_tests_table_html(self, tests, failed_tests=False):
tests_type = "Failed tests" if failed_tests else "Passed tests"
html = "<h3>%s (%d)</h3>" % (tests_type, len(tests))
@ -121,11 +73,6 @@ class GithubHtmlReport:
html += "</td></tr>"
return html
def get_sauce_job_url(self, job_id):
token = hmac.new(bytes(self.sauce_username + ":" + self.sauce_access_key, 'latin-1'),
bytes(job_id, 'latin-1'), md5).hexdigest()
return "https://saucelabs.com/jobs/%s?auth=%s" % (job_id, token)
def build_device_sessions_html(self, jobs):
html = "<ins>Device sessions:</ins>"
html += "<p><ul>"

View File

@ -0,0 +1,72 @@
import json
import requests
import base64
from os import environ
from support.base_test_report import BaseTestReport
class TestrailReport(BaseTestReport):
def __init__(self, sauce_username, sauce_access_key):
super(TestrailReport, self).__init__(sauce_username, sauce_access_key)
self.password = environ.get('TESTRAIL_PASS')
self.user = environ.get('TESTRAIL_USER')
self.run_id = None
self.suite_id = 42
self.project_id = 9
self.outcomes = {
'passed': 1,
'undefined_fail': 10}
self.headers = dict()
self.headers['Authorization'] = 'Basic %s' % str(
base64.b64encode(bytes('%s:%s' % (self.user, self.password), 'utf-8')), 'ascii').strip()
self.headers['Content-Type'] = 'application/json'
self.url = 'https://ethstatus.testrail.net/index.php?/api/v2/'
def get(self, method):
raw_response = requests.get(self.url + method, headers=self.headers).text
return json.loads(raw_response)
def post(self, method, data):
data = bytes(json.dumps(data), 'utf-8')
raw_response = requests.post(self.url + method, data=data, headers=self.headers).text
return json.loads(raw_response)
def get_suites(self):
return self.get('get_suites/%s' % self.project_id)
def get_tests(self):
return self.get('get_tests/%s' % self.run_id)
def get_milestones(self):
return self.get('get_milestones/%s' % self.project_id)
@property
def actual_milestone_id(self):
return self.get_milestones()[-1]['id']
def add_run(self, run_name):
request_body = {'suite_id': self.suite_id,
'name': run_name,
'milestone_id': self.actual_milestone_id}
run = self.post('add_run/%s' % self.project_id, request_body)
self.run_id = run['id']
def add_results(self):
for test in self.get_all_tests():
test_steps = "# Steps: \n"
devices = str()
method = 'add_result_for_case/%s/%s' % (self.run_id, test.testrail_case_id)
for step in test.steps:
test_steps += step + "\n"
for i, device in enumerate(test.jobs):
devices += "# [Device %d](%s) \n" % (i + 1, self.get_sauce_job_url(device))
data = {'status_id': self.outcomes['undefined_fail'] if test.error else self.outcomes['passed'],
'comment': '%s' % ('# Error: \n %s \n' % test.error) + devices + test_steps if test.error
else devices + test_steps}
self.post(method, data=data)

View File

@ -24,7 +24,8 @@ def info(text: str):
class SingleTestData(object):
def __init__(self, name, steps=list(), jobs=list(), error=None):
def __init__(self, name, steps=list(), jobs=list(), error=None, testrail_case_id=None):
self.testrail_case_id = testrail_case_id
self.name = name
self.steps = steps
self.jobs = jobs

View File

@ -5,7 +5,8 @@ from datetime import datetime
from os import environ
from io import BytesIO
from sauceclient import SauceClient
from support.github_test_report import GithubHtmlReport
from support.github_report import GithubHtmlReport
from support.testrail_report import TestrailReport
sauce_username = environ.get('SAUCE_USERNAME')
sauce_access_key = environ.get('SAUCE_ACCESS_KEY')
@ -13,6 +14,7 @@ github_token = environ.get('GIT_HUB_TOKEN')
sauce = SauceClient(sauce_username, sauce_access_key)
github_report = GithubHtmlReport(sauce_username, sauce_access_key)
testrail_report = TestrailReport(sauce_username, sauce_access_key)
def pytest_addoption(parser):
@ -36,6 +38,10 @@ def pytest_addoption(parser):
action='store',
default=None,
help='Pull Request number')
parser.addoption('--nightly',
action='store',
default=False,
help='boolean; For running extended test suite against nightly build')
def is_master(config):
@ -55,31 +61,34 @@ def pytest_configure(config):
logging.basicConfig(level=logging.INFO)
test_suite_data.apk_name = ([i for i in [i for i in config.getoption('apk').split('/')
if '.apk' in i]])[0]
if is_master(config) and config.getoption('env') == 'sauce':
if config.getoption('pr_number'):
with open('github_comment.txt', 'w') as _:
pass
if not is_uploaded():
if 'http' in config.getoption('apk'):
response = requests.get(config.getoption('apk'), stream=True)
response.raise_for_status()
file = BytesIO(response.content)
del response
requests.post('http://saucelabs.com/rest/v1/storage/'
+ sauce_username + '/' + test_suite_data.apk_name + '?overwrite=true',
auth=(sauce_username, sauce_access_key),
data=file,
headers={'Content-Type': 'application/octet-stream'})
else:
sauce.storage.upload_file(config.getoption('apk'))
if is_master(config):
if config.getoption('nightly'):
testrail_report.add_run(test_suite_data.apk_name)
if config.getoption('env') == 'sauce':
if not is_uploaded():
if 'http' in config.getoption('apk'):
response = requests.get(config.getoption('apk'), stream=True)
response.raise_for_status()
file = BytesIO(response.content)
del response
requests.post('http://saucelabs.com/rest/v1/storage/'
+ sauce_username + '/' + test_suite_data.apk_name + '?overwrite=true',
auth=(sauce_username, sauce_access_key),
data=file,
headers={'Content-Type': 'application/octet-stream'})
else:
sauce.storage.upload_file(config.getoption('apk'))
def pytest_unconfigure(config):
if is_master(config) and config.getoption('pr_number'):
from github import Github
repo = Github(github_token).get_user('status-im').get_repo('status-react')
pull = repo.get_pull(int(config.getoption('pr_number')))
pull.create_issue_comment(github_report.build_html_report())
if is_master(config):
if config.getoption('pr_number'):
from github import Github
repo = Github(github_token).get_user('status-im').get_repo('status-react')
pull = repo.get_pull(int(config.getoption('pr_number')))
pull.create_issue_comment(github_report.build_html_report())
if config.getoption('nightly'):
testrail_report.add_results()
@pytest.mark.hookwrapper
@ -101,5 +110,10 @@ def update_sauce_jobs(test_name, job_ids, passed):
sauce.jobs.update_job(job_id, name=test_name, passed=passed)
def get_testrail_case_id(obj):
if 'testrail_case_id' in obj.keywords._markers:
return obj.keywords._markers['testrail_case_id'].args[0]
def pytest_runtest_setup(item):
test_suite_data.add_test(SingleTestData(item.name))
test_suite_data.add_test(SingleTestData(item.name, testrail_case_id=get_testrail_case_id(item)))

View File

@ -0,0 +1,7 @@
import pytest
pr = pytest.mark.pr
testrail_case_id = pytest.mark.testrail_case_id
chat = pytest.mark.chat
all = pytest.mark.all
transaction = pytest.mark.transaction

View File

@ -4,7 +4,7 @@ import pytest
import emoji
from tests.base_test_case import MultipleDeviceTestCase
from tests import group_chat_users, get_current_time
from tests import group_chat_users, get_current_time, marks
from views.sign_in_view import SignInView
unicode_text_message = '%s%s%s%s %s%s%s%s%s%s%s' % (chr(355), chr(275), chr(353), chr(539), chr(1084), chr(949),
@ -17,11 +17,12 @@ emoji_unicode_1 = emoji.EMOJI_UNICODE[emoji_name_1]
message_with_new_line = 'message' '\n' 'with new line'
@pytest.mark.all
@pytest.mark.chat
@marks.all
@marks.chat
class TestMessages(MultipleDeviceTestCase):
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3390)
def test_one_to_one_chat_messages(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])
@ -77,7 +78,8 @@ class TestMessages(MultipleDeviceTestCase):
self.verify_no_errors()
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3391)
def test_group_chat_messages_and_delete_chat(self):
self.create_drivers(3)
@ -139,7 +141,8 @@ class TestMessages(MultipleDeviceTestCase):
self.verify_no_errors()
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3392)
def test_public_chat(self):
self.create_drivers(2)
device_1, device_2 = SignInView(self.drivers[0]), SignInView(self.drivers[1])

View File

@ -3,14 +3,14 @@ import emoji
import pytest
import time
from tests.base_test_case import SingleDeviceTestCase
from tests import basic_user
from tests import basic_user, marks
from views.sign_in_view import SignInView
@pytest.mark.all
@marks.all
class TestProfileView(SingleDeviceTestCase):
@pytest.mark.testrail_case_id(3395)
@marks.testrail_case_id(3395)
def test_qr_code_and_its_value(self):
sign_in_view = SignInView(self.driver)
sign_in_view.create_user()
@ -27,12 +27,12 @@ class TestProfileView(SingleDeviceTestCase):
wallet_view.qr_code_image.wait_for_element()
key_value = wallet_view.address_text.text
key_value_from_qr = wallet_view.get_text_from_qr()
if key_value_from_qr != "ethereum:%s'" % key_value:
if key_value not in key_value_from_qr:
self.errors.append(
"Wallet QR code value '%s' doesn't match wallet address '%s'" % (key_value_from_qr, key_value))
self.verify_no_errors()
@pytest.mark.pr
@marks.pr
@pytest.mark.testrail_case_id(3396)
def test_contact_profile_view(self):
sign_in_view = SignInView(self.driver)
@ -51,7 +51,8 @@ class TestProfileView(SingleDeviceTestCase):
chat_view.profile_send_transaction.click()
assert chat_view.chat_message_input.text.strip() == '/send'
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3397)
def test_network_switch(self):
sign_in_view = SignInView(self.driver)
sign_in_view.create_user()
@ -100,7 +101,7 @@ class TestProfileView(SingleDeviceTestCase):
profile_view.logout_button.click()
profile_view.confirm_logout_button.click()
recover_access_view = sign_in_view.add_existing_account_button.click()
recover_access_view.passphrase_input.set_value(' '.join(seed_phrase.values()))
recover_access_view.passphrase_input.set_value(' '.join(seed_phrase[key] for key in sorted(seed_phrase)))
recover_access_view.password_input.set_value('qwerty1234')
recover_access_view.sign_in_button.click()
sign_in_view.do_not_share.click()

View File

@ -1,16 +1,18 @@
import pytest
import time
from tests.base_test_case import SingleDeviceTestCase, MultipleDeviceTestCase
from tests import transaction_users, api_requests, get_current_time, transaction_users_wallet
from tests import transaction_users, api_requests, get_current_time, transaction_users_wallet, marks
from selenium.common.exceptions import TimeoutException
from views.sign_in_view import SignInView
@pytest.mark.all
@marks.all
@marks.transaction
class TestTransaction(SingleDeviceTestCase):
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3401)
def test_transaction_send_command_one_to_one_chat(self):
recipient = transaction_users['B_USER']
sign_in_view = SignInView(self.driver)
@ -32,7 +34,8 @@ class TestTransaction(SingleDeviceTestCase):
transactions_view = wallet_view.transactions_button.click()
transactions_view.transactions_table.find_transaction(amount=transaction_amount)
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3402)
def test_transaction_send_command_wrong_password(self):
sender = transaction_users['A_USER']
recipient = transaction_users['B_USER']
@ -53,7 +56,8 @@ class TestTransaction(SingleDeviceTestCase):
send_transaction_view.sign_transaction_button.click()
send_transaction_view.find_full_text('Wrong password', 20)
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3403)
def test_transaction_send_command_group_chat(self):
recipient = transaction_users['A_USER']
sign_in_view = SignInView(self.driver)
@ -72,7 +76,8 @@ class TestTransaction(SingleDeviceTestCase):
chat_view.send_transaction_in_group_chat(transaction_amount, 'qwerty1234', recipient)
api_requests.verify_balance_is_updated(initial_recipient_balance, recipient['address'])
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3404)
def test_send_transaction_from_daap(self):
sender = transaction_users['B_USER']
sign_in_view = SignInView(self.driver)
@ -100,7 +105,8 @@ class TestTransaction(SingleDeviceTestCase):
api_requests.verify_balance_is_updated(initial_balance, address)
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3405)
def test_send_eth_from_wallet_sign_later(self):
sender = transaction_users_wallet['B_USER']
recipient = transaction_users_wallet['A_USER']
@ -134,7 +140,8 @@ class TestTransaction(SingleDeviceTestCase):
transactions_view.history_tab.click()
transactions_view.transactions_table.find_transaction(amount=amount)
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3406)
def test_send_stt_from_wallet_via_enter_recipient_address(self):
sender = transaction_users_wallet['A_USER']
recipient = transaction_users_wallet['B_USER']
@ -159,7 +166,8 @@ class TestTransaction(SingleDeviceTestCase):
send_transaction.sign_transaction_button.click()
send_transaction.got_it_button.click()
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3407)
def test_send_eth_from_wallet_sign_now(self):
recipient = transaction_users['F_USER']
sender = transaction_users['E_USER']
@ -184,10 +192,12 @@ class TestTransaction(SingleDeviceTestCase):
send_transaction.got_it_button.click()
@pytest.mark.all
@marks.all
@marks.transaction
class TestTransactions(MultipleDeviceTestCase):
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3408)
def test_send_eth_to_request_in_group_chat(self):
recipient = transaction_users['E_USER']
sender = self.senders['f_user'] = transaction_users['F_USER']
@ -216,7 +226,8 @@ class TestTransactions(MultipleDeviceTestCase):
device_2_chat.send_eth_to_request(request_button, sender['password'])
api_requests.verify_balance_is_updated(initial_balance_recipient, recipient['address'])
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3409)
def test_send_eth_to_request_in_one_to_one_chat(self):
recipient = transaction_users['C_USER']
sender = self.senders['d_user'] = transaction_users['D_USER']
@ -255,7 +266,8 @@ class TestTransactions(MultipleDeviceTestCase):
transactions_view = device_2_wallet.transactions_button.click()
transactions_view.transactions_table.find_transaction(amount=amount)
@pytest.mark.pr
@marks.pr
@marks.testrail_case_id(3410)
def test_send_eth_to_request_from_wallet(self):
recipient = transaction_users_wallet['D_USER']
sender = self.senders['c_user'] = transaction_users['C_USER']

View File

@ -208,8 +208,8 @@ class BaseView(object):
'k': 39, 'l': 40, 'm': 41, 'n': 42, 'o': 43, 'p': 44, 'q': 45, 'r': 46, 's': 47, 't': 48,
'u': 49, 'v': 50, 'w': 51, 'x': 52, 'y': 53, 'z': 54}
time.sleep(3)
info("Enter '%s' using native keyboard" % string)
for i in string:
info("Tap '%s' on native keyboard" % i)
if type(keys[i]) is list:
keycode, metastate = keys[i][0], keys[i][1]
else: