Desktop automation PoC
Signed-off-by: yevh-berdnyk <ie.berdnyk@gmail.com>
This commit is contained in:
parent
d823bfd71c
commit
55d72b534a
|
@ -0,0 +1,2 @@
|
||||||
|
[pytest]
|
||||||
|
addopts = -s -v --junitxml=result.xml --tb=line
|
|
@ -0,0 +1,16 @@
|
||||||
|
atomicwrites==1.2.1
|
||||||
|
attrs==18.2.0
|
||||||
|
certifi==2018.8.24
|
||||||
|
chardet==3.0.4
|
||||||
|
funcsigs==1.0.2
|
||||||
|
idna==2.7
|
||||||
|
jip==0.9.13
|
||||||
|
more-itertools==4.3.0
|
||||||
|
pathlib2==2.3.2
|
||||||
|
pluggy==0.7.1
|
||||||
|
py==1.6.0
|
||||||
|
pytest==2.7.2
|
||||||
|
requests==2.19.1
|
||||||
|
scandir==1.9.0
|
||||||
|
six==1.11.0
|
||||||
|
urllib3==1.23
|
|
@ -0,0 +1,52 @@
|
||||||
|
import org.sikuli.script.SikulixForJython
|
||||||
|
import pytest
|
||||||
|
from sikuli import *
|
||||||
|
from subprocess import check_output
|
||||||
|
|
||||||
|
|
||||||
|
def mac_os_setup():
|
||||||
|
check_output(['hdiutil', 'attach', 'nightly.dmg'])
|
||||||
|
check_output(['cp', '-rf', '/Volumes/Status/Status.app', '/Applications/'])
|
||||||
|
check_output(['hdiutil', 'detach', '/Volumes/Status/'])
|
||||||
|
import time
|
||||||
|
time.sleep(10)
|
||||||
|
openApp('Status.app')
|
||||||
|
|
||||||
|
|
||||||
|
def mac_os_teardown():
|
||||||
|
closeApp('Status.app')
|
||||||
|
for dir in '/Applications/Status.app', '/Library/Application\ Support/StatusIm', \
|
||||||
|
'/Users/yberdnyk/Library/Caches/StatusIm':
|
||||||
|
check_output(['rm', '-rf', dir])
|
||||||
|
|
||||||
|
|
||||||
|
def linux_setup():
|
||||||
|
check_output(['chmod', '+x', './nightly.AppImage'])
|
||||||
|
check_output(['./nightly.AppImage', '--appimage-extract'])
|
||||||
|
check_output(['chmod', '+x', '/src/squashfs-root/AppRun'])
|
||||||
|
openApp('/src/squashfs-root/AppRun')
|
||||||
|
|
||||||
|
|
||||||
|
def linux_teardown():
|
||||||
|
pass
|
||||||
|
# check_output(['killall', 'ubuntu-server'])
|
||||||
|
# check_output(['rm', '-rf', '~/.local/share/StatusIm/'])
|
||||||
|
# check_output(['rm', '-rf', '~/.cache/StatusIm/'])
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTestCase:
|
||||||
|
Settings.ActionLogs = 0
|
||||||
|
Settings.OcrTextSearch = True
|
||||||
|
Settings.OcrTextRead = True
|
||||||
|
|
||||||
|
def setup_method(self, method):
|
||||||
|
if pytest.config.getoption('os') == 'linux':
|
||||||
|
linux_setup()
|
||||||
|
else:
|
||||||
|
mac_os_setup()
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
if pytest.config.getoption('os') == 'linux':
|
||||||
|
linux_teardown()
|
||||||
|
else:
|
||||||
|
mac_os_teardown()
|
|
@ -0,0 +1,56 @@
|
||||||
|
import org.sikuli.script.SikulixForJython
|
||||||
|
from sikuli import *
|
||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
from urllib import urlretrieve
|
||||||
|
from subprocess import check_output
|
||||||
|
|
||||||
|
from tests.report import save_test_result, TEST_REPORT_DIR
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption('--os',
|
||||||
|
action='store',
|
||||||
|
default='linux')
|
||||||
|
parser.addoption("--nightly",
|
||||||
|
action="store",
|
||||||
|
default=True)
|
||||||
|
parser.addoption('--dmg',
|
||||||
|
action='store',
|
||||||
|
default=None,
|
||||||
|
help='Url or local path to dmg')
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
if config.getoption('nightly'):
|
||||||
|
raw_data = requests.request('GET', 'https://status-im.github.io/nightly/').text
|
||||||
|
if config.getoption('os') == 'linux':
|
||||||
|
app_url = re.findall('href="(.*AppImage)', raw_data)[0]
|
||||||
|
urlretrieve(app_url, 'nightly.AppImage')
|
||||||
|
else:
|
||||||
|
dmg_url = re.findall('href="(.*dmg)', raw_data)[0]
|
||||||
|
urlretrieve(dmg_url, 'nightly.dmg')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.hookwrapper
|
||||||
|
def pytest_runtest_makereport(item, call):
|
||||||
|
import logging
|
||||||
|
# logging.basicConfig(filename='%s/%s.log' % (TEST_REPORT_DIR, item.name), filemode='w', level=logging.INFO)
|
||||||
|
outcome = yield
|
||||||
|
report = outcome.get_result()
|
||||||
|
if report.when == 'call':
|
||||||
|
pass
|
||||||
|
# save_test_result(item, report)
|
||||||
|
|
||||||
|
|
||||||
|
def after_all():
|
||||||
|
if pytest.config.getoption('os') == 'linux':
|
||||||
|
check_output(['rm', '-rf', 'nightly.AppImage'])
|
||||||
|
else:
|
||||||
|
check_output(['rm', '-rf', 'nightly.dmg'])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
|
def before_all(request):
|
||||||
|
request.addfinalizer(finalizer=after_all)
|
|
@ -0,0 +1,33 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from subprocess import check_output
|
||||||
|
|
||||||
|
TEST_REPORT_DIR = "%s/../report" % os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_report_file_path(test_name):
|
||||||
|
file_name = "%s.json" % test_name
|
||||||
|
if not os.path.exists(TEST_REPORT_DIR):
|
||||||
|
os.makedirs(TEST_REPORT_DIR)
|
||||||
|
return os.path.join(TEST_REPORT_DIR, file_name)
|
||||||
|
|
||||||
|
|
||||||
|
def get_testrail_case_id(obj):
|
||||||
|
if 'testrail_id' in obj.keywords._markers:
|
||||||
|
return obj.keywords._markers['testrail_id'].args[0]
|
||||||
|
|
||||||
|
|
||||||
|
def save_test_result(test, report):
|
||||||
|
test_name = test.name
|
||||||
|
file_path = get_test_report_file_path(test_name)
|
||||||
|
if report.failed:
|
||||||
|
check_output(['screencapture', '%s/%s.png' % (TEST_REPORT_DIR, test_name)])
|
||||||
|
with open('%s/%s.log' % (TEST_REPORT_DIR, test_name), 'r') as log:
|
||||||
|
steps = [i for i in log]
|
||||||
|
test_dict = {
|
||||||
|
'testrail_case_id': get_testrail_case_id(test),
|
||||||
|
'name': test_name,
|
||||||
|
'steps': steps,
|
||||||
|
'error': str(report.longrepr.reprcrash.message),
|
||||||
|
'screenshot': test_name + '.png'}
|
||||||
|
json.dump(test_dict, open(file_path, 'w'))
|
|
@ -0,0 +1,16 @@
|
||||||
|
from tests.base_test_case import BaseTestCase
|
||||||
|
from views.sign_in_view import SignInView
|
||||||
|
|
||||||
|
|
||||||
|
class TestCreateAccount(BaseTestCase):
|
||||||
|
|
||||||
|
def test_create_account(self):
|
||||||
|
sign_in = SignInView()
|
||||||
|
sign_in.create_account_button.click()
|
||||||
|
sign_in.password_input.input_value('123456')
|
||||||
|
sign_in.next_button.click()
|
||||||
|
sign_in.confirm_password_input.input_value('123456')
|
||||||
|
sign_in.next_button.click()
|
||||||
|
sign_in.username_input.input_value('test')
|
||||||
|
sign_in.next_button.click()
|
||||||
|
sign_in.home_button.find_element()
|
|
@ -0,0 +1,27 @@
|
||||||
|
import logging
|
||||||
|
import org.sikuli.script.SikulixForJython
|
||||||
|
import pytest
|
||||||
|
from sikuli import *
|
||||||
|
|
||||||
|
|
||||||
|
class BaseElement(object):
|
||||||
|
def __init__(self, screenshot):
|
||||||
|
self.screenshot = screenshot
|
||||||
|
|
||||||
|
def find_element(self):
|
||||||
|
try:
|
||||||
|
wait(self.screenshot, 10)
|
||||||
|
except FindFailed:
|
||||||
|
pytest.fail('%s was not found' % self.__class__.__name__)
|
||||||
|
|
||||||
|
def click(self):
|
||||||
|
logging.info('Click %s' % self.__class__.__name__)
|
||||||
|
self.find_element()
|
||||||
|
click(self.screenshot)
|
||||||
|
|
||||||
|
|
||||||
|
class InputField(BaseElement):
|
||||||
|
|
||||||
|
def input_value(self, value):
|
||||||
|
self.click()
|
||||||
|
type(value)
|
|
@ -0,0 +1,17 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from views.base_element import BaseElement
|
||||||
|
|
||||||
|
IMAGES_PATH = os.path.join(os.path.dirname(__file__), 'images/base_view')
|
||||||
|
|
||||||
|
|
||||||
|
class HomeButton(BaseElement):
|
||||||
|
def __init__(self):
|
||||||
|
super(HomeButton, self).__init__(IMAGES_PATH + '/home_button.png')
|
||||||
|
|
||||||
|
|
||||||
|
class BaseView(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(BaseView, self).__init__()
|
||||||
|
self.home_button = HomeButton()
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,41 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from views.base_element import BaseElement, InputField
|
||||||
|
from views.base_view import BaseView
|
||||||
|
|
||||||
|
IMAGES_PATH = os.path.join(os.path.dirname(__file__), 'images/sign_in_view')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAccountButton(BaseElement):
|
||||||
|
def __init__(self):
|
||||||
|
super(CreateAccountButton, self).__init__(IMAGES_PATH + '/create_account.png')
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordInput(InputField):
|
||||||
|
def __init__(self):
|
||||||
|
super(PasswordInput, self).__init__(IMAGES_PATH + '/password_input.png')
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmPasswordInput(InputField):
|
||||||
|
def __init__(self):
|
||||||
|
super(ConfirmPasswordInput, self).__init__(IMAGES_PATH + '/confirm_password_input.png')
|
||||||
|
|
||||||
|
|
||||||
|
class UserNameInput(InputField):
|
||||||
|
def __init__(self):
|
||||||
|
super(UserNameInput, self).__init__(IMAGES_PATH + '/username_input.png')
|
||||||
|
|
||||||
|
|
||||||
|
class NextButton(InputField):
|
||||||
|
def __init__(self):
|
||||||
|
super(NextButton, self).__init__(IMAGES_PATH + '/next_button.png')
|
||||||
|
|
||||||
|
|
||||||
|
class SignInView(BaseView):
|
||||||
|
def __init__(self):
|
||||||
|
super(SignInView, self).__init__()
|
||||||
|
self.create_account_button = CreateAccountButton()
|
||||||
|
self.password_input = PasswordInput()
|
||||||
|
self.confirm_password_input = ConfirmPasswordInput()
|
||||||
|
self.username_input = UserNameInput()
|
||||||
|
self.next_button = NextButton()
|
Loading…
Reference in New Issue