diff --git a/test/desktop_sikuli/main.py b/test/desktop_sikuli/main.py index c429481c78..1262e7954d 100644 --- a/test/desktop_sikuli/main.py +++ b/test/desktop_sikuli/main.py @@ -2,6 +2,8 @@ import docker import pytest import argparse import os +import re +import requests from datetime import datetime from urllib.request import urlretrieve from tests.report import TEST_REPORT_DIR_CONTAINER @@ -12,10 +14,12 @@ def main(**kwargs): if not os.path.exists(kwargs['test_results_path']): os.makedirs(kwargs['test_results_path']) - app_version = None if kwargs['linux_app_url']: - app_version = ([i for i in [i for i in kwargs['linux_app_url'].split('/') if '.AppImage' in i]])[0] - urlretrieve(kwargs['linux_app_url'], 'nightly.AppImage') + linux_app_url = kwargs['linux_app_url'] + else: + linux_app_url = re.findall('https\S*AppImage', requests.get('https://status.im/nightly/').text)[0] + app_version = ([i for i in [i for i in linux_app_url.split('/') if '.AppImage' in i]])[0] + urlretrieve(linux_app_url, 'nightly.AppImage') client = docker.from_env() client.images.build(tag='status_desktop', path='.') pytest.main(['--collect-only', '-m %s' % kwargs['mark']]) @@ -55,7 +59,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--test_results_path', action='store', - default='/Users/adan/Desktop/desktop_e2e_results') + default=os.path.dirname(os.path.abspath(__file__))) parser.add_argument('--linux_app_url', action='store', default=None) diff --git a/test/desktop_sikuli/tests/base_test_case.py b/test/desktop_sikuli/tests/base_test_case.py index 0168972ac9..fe9589d23d 100644 --- a/test/desktop_sikuli/tests/base_test_case.py +++ b/test/desktop_sikuli/tests/base_test_case.py @@ -1,3 +1,5 @@ +import pytest + try: import org.sikuli.script.SikulixForJython from sikuli import * @@ -6,6 +8,7 @@ except Exception: class BaseTestCase: + errors = list() try: Settings.ActionLogs = 0 @@ -18,3 +21,7 @@ class BaseTestCase: def teardown_method(self, method): pass + + def verify_no_errors(self): + if self.errors: + pytest.fail('. '.join([self.errors.pop(0) for _ in range(len(self.errors))])) diff --git a/test/desktop_sikuli/tests/test_create_account.py b/test/desktop_sikuli/tests/test_create_account.py index fd0e5c46e0..6b5074b781 100644 --- a/test/desktop_sikuli/tests/test_create_account.py +++ b/test/desktop_sikuli/tests/test_create_account.py @@ -64,3 +64,11 @@ class TestCreateAccount(BaseTestCase): sign_in.press_enter() sign_in.profile_button.click() profile.find_text('user_2') + + @pytest.mark.testrail_id(5650) + def test_status_log(self): + sign_in = SignInView() + sign_in.create_account() + with open('/root/.local/share/Status/Status.log') as f: + if 'mnemonic' in f.read(): + pytest.fail("'mnemonic' is in Status.log!") diff --git a/test/desktop_sikuli/tests/test_profile.py b/test/desktop_sikuli/tests/test_profile.py index 2122ec71d0..939b92fa76 100644 --- a/test/desktop_sikuli/tests/test_profile.py +++ b/test/desktop_sikuli/tests/test_profile.py @@ -6,13 +6,15 @@ import pytest class TestProfile(BaseTestCase): + @pytest.mark.testrail_id(5590) def test_copy_contact_code(self): sign_in = SignInView() sign_in.recover_access(base_user['passphrase']) profile = sign_in.profile_button.click() profile.share_my_code_button.click() profile.copy_code_button.click() - assert profile.get_clipboard() == base_user['public_key'] + if profile.get_clipboard() != base_user['public_key']: + pytest.fail('Contact code was not copied to clipboard') @pytest.mark.skip('Test cases is not ready yet') def test_change_mail_server(self): @@ -23,3 +25,33 @@ class TestProfile(BaseTestCase): s_names = profile.get_mail_servers_list() profile.get_mail_server(s_names[0]).is_active() + @pytest.mark.testrail_id(5593) + def test_log_out(self): + sign_in = SignInView() + username = 'test_log_out' + sign_in.create_account(username=username) + profile = sign_in.profile_button.click() + profile.log_out_button.click() + sign_in.password_input.find_element() + for text in username, 'Sign in to Status', 'Other accounts': + if not sign_in.element_by_text(text).is_visible(): + pytest.fail("Text '%s' is not shown" % text) + + @pytest.mark.skip + # @pytest.mark.testrail_id(5648) + def test_back_up_recovery_phrase(self): + sign_in = SignInView() + sign_in.create_account() + profile = sign_in.profile_button.click() + profile.back_up_recovery_phrase_button.click() + profile.ok_continue_button.click() + recovery_phrase = profile.get_recovery_phrase() + profile.next_button.click() + word_number = profile.get_recovery_phrase_word_number() + profile.recovery_phrase_word_input.input_value(recovery_phrase[word_number]) + profile.next_button.click() + word_number_1 = profile.get_recovery_phrase_word_number() + profile.recovery_phrase_word_input.input_value(recovery_phrase[word_number_1]) + profile.done_button.click() + profile.yes_button.click() + profile.find_text("You're all set!") diff --git a/test/desktop_sikuli/tests/test_recover_access.py b/test/desktop_sikuli/tests/test_recover_access.py index f704f006ff..10e9a60b4c 100644 --- a/test/desktop_sikuli/tests/test_recover_access.py +++ b/test/desktop_sikuli/tests/test_recover_access.py @@ -1,3 +1,6 @@ +import pytest + +from utilities import passpharse_with_spaces from tests import base_user from tests.base_test_case import BaseTestCase from views.sign_in_view import SignInView @@ -16,6 +19,7 @@ class TestRecoverAccess(BaseTestCase): profile.share_my_code_button.click() profile.find_text(base_user['public_key']) + @pytest.mark.testrail_id(5571) def test_recover_access_proceed_with_enter(self): sign_in = SignInView() sign_in.i_have_account_button.click() @@ -26,9 +30,55 @@ class TestRecoverAccess(BaseTestCase): sign_in.press_enter() sign_in.home_button.find_element() + @pytest.mark.testrail_id(5569) def test_recover_access_go_back(self): sign_in = SignInView() sign_in.i_have_account_button.click() sign_in.recovery_phrase_input.find_element() sign_in.back_button.click() sign_in.create_account_button.find_element() + + @pytest.mark.testrail_id(5649) + def test_recovery_phrase_for_recovered_account(self): + sign_in = SignInView() + sign_in.recover_access(base_user['passphrase']) + profile = sign_in.profile_button.click() + if profile.share_my_code_button.is_visible(): + profile.element_by_text('Backup').verify_element_is_not_present() + else: + pytest.fail('Profile view was not opened') + + @pytest.mark.testrail_id(5652) + def test_recover_account_error_messages(self): + errors = {'': 'Required field', + ' '.join(base_user['passphrase'].split()[::-1]): 'Some words might be misspelled', + ' '.join(base_user['passphrase'].split()[:-1]) + ' aaa': 'Some words might be misspelled', + 'robot seed robot seed robot seed robot seed robot seed robot seed.': 'Recovery phrase is invalid', + 'robot': 'Recovery phrase is invalid', + 'seed seed seed seed seed seed seed seed seed seed seed': 'Recovery phrase is invalid', + 'seed seed seed seed seed seed seed seed seed seed seed seed seed': 'Recovery phrase is invalid' + } + sign_in = SignInView() + for i in errors: + sign_in.i_have_account_button.click() + sign_in.recovery_phrase_input.send_keys(i) + sign_in.recover_password_input.click() + error_message = errors[i] + if not sign_in.element_by_text(error_message).is_visible(): + self.errors.append("Error message '%s' is not shown for passphrase '%s'" % (error_message, i)) + sign_in.back_button.click() + sign_in.i_have_account_button.click() + sign_in.recovery_phrase_input.send_keys(base_user['passphrase']) + sign_in.recover_password_input.click() + sign_in.sign_in_button.click() + if not sign_in.element_by_text('Required field').is_visible(): + self.errors.append("Error message 'Required field' is not shown for empty password input") + self.verify_no_errors() + + @pytest.mark.testrail_id(5651) + def test_recover_account_with_spaces(self): + sign_in = SignInView() + passphrase = passpharse_with_spaces(base_user['passphrase']) + sign_in.recover_access(passphrase) + profile = sign_in.profile_button.click() + profile.find_text(base_user['username']) diff --git a/test/desktop_sikuli/utilities.py b/test/desktop_sikuli/utilities.py new file mode 100644 index 0000000000..6fadbef658 --- /dev/null +++ b/test/desktop_sikuli/utilities.py @@ -0,0 +1,6 @@ +import itertools + + +def passpharse_with_spaces(passphrase): + phrase_list = passphrase.split() + return ''.join(list(itertools.chain.from_iterable(zip(phrase_list, [' ' * i for i in range(1, 13)])))) diff --git a/test/desktop_sikuli/views/base_element.py b/test/desktop_sikuli/views/base_element.py index 12586037df..6d5b06d7fb 100644 --- a/test/desktop_sikuli/views/base_element.py +++ b/test/desktop_sikuli/views/base_element.py @@ -2,6 +2,7 @@ import logging import time import pytest import re + try: import org.sikuli.script.SikulixForJython from sikuli import * @@ -10,15 +11,16 @@ except Exception: class BaseElement(object): - def __init__(self, screenshot): + def __init__(self, screenshot, x=0, y=0, w=1024, h=768): self.screenshot = screenshot self.name = re.findall('([^\/]+)(?=.png)', self.screenshot)[0].replace('_', ' ').title() + self.region = Region(x, y, w, h) def find_element(self, log=True): if log: logging.info('Find %s' % self.name) try: - wait(self.screenshot, 10) + self.region.wait(self.screenshot, 10) except FindFailed: pytest.fail('%s was not found' % self.name) @@ -26,16 +28,27 @@ class BaseElement(object): if log: logging.info('Click %s' % self.name) self.find_element(log=False) - click(self.screenshot) + self.region.click(self.screenshot) + + def is_visible(self): + try: + self.region.wait(self.screenshot, 10) + return True + except FindFailed: + return False def verify_element_is_not_present(self): logging.info('Verify: %s is not present' % self.name) try: - wait(self.screenshot, 10) + self.region.wait(self.screenshot, 10) pytest.fail('%s is displayed but not expected' % self.name) except FindFailed: pass + def get_target(self): + self.find_element(log=False) + return self.region.find(self.screenshot).getTarget() + class InputField(BaseElement): @@ -53,7 +66,7 @@ class InputField(BaseElement): def is_focused(self): self.find_element(log=False) - return find(self.screenshot).getTarget() == Env.getMouseLocation() + return self.get_target() == Env.getMouseLocation() def verify_is_focused(self): logging.info('Verify %s is focused' % self.name) @@ -63,14 +76,36 @@ class InputField(BaseElement): class TextElement(object): def __init__(self, text): + self.text = text self.element_line = None + + def find_element(self): for _ in range(3): lines = collectLines() for line in lines: - if text in line.getText().encode('ascii', 'ignore'): + if self.text in line.getText().encode('ascii', 'ignore'): self.element_line = line return time.sleep(3) + pytest.fail("Element with text '%s' was not found" % self.text) def click(self): + logging.info("Click %s button" % self.text) + self.find_element() self.element_line.click() + + def get_whole_text(self): + self.find_element() + return self.element_line.getText().encode('ascii', 'ignore') + + def is_visible(self): + from _pytest.runner import Failed + try: + self.find_element() + return True + except Failed: + return False + + def verify_element_is_not_present(self): + if self.is_visible(): + pytest.fail("'%s text is displayed but not expected'" % self.text) diff --git a/test/desktop_sikuli/views/base_view.py b/test/desktop_sikuli/views/base_view.py index 1324501af0..a7a543edee 100644 --- a/test/desktop_sikuli/views/base_view.py +++ b/test/desktop_sikuli/views/base_view.py @@ -1,4 +1,5 @@ import logging + try: import org.sikuli.script.SikulixForJython from sikuli import * @@ -28,7 +29,11 @@ class BaseView(object): super(BaseView, self).__init__() self.home_button = BaseElement(IMAGES_PATH + '/home_button.png') self.profile_button = ProfileButton() - self.back_button = BaseElement(IMAGES_PATH + '/back_button.png') + self.back_button = BaseElement(IMAGES_PATH + '/back_button.png', x=10, y=20, w=50, h=50) + self.ok_button = BaseElement(IMAGES_PATH + '/ok_button.png') + self.next_button = BaseElement(IMAGES_PATH + '/next_button.png') + self.done_button = BaseElement(IMAGES_PATH + '/done_button.png') + self.yes_button = BaseElement(IMAGES_PATH + '/yes_button.png') def find_text(self, expected_text): logging.info("Find text '%s'" % expected_text) diff --git a/test/desktop_sikuli/views/images/base_view/done_button.png b/test/desktop_sikuli/views/images/base_view/done_button.png new file mode 100644 index 0000000000..62a4098f91 Binary files /dev/null and b/test/desktop_sikuli/views/images/base_view/done_button.png differ diff --git a/test/desktop_sikuli/views/images/base_view/next_button.png b/test/desktop_sikuli/views/images/base_view/next_button.png new file mode 100644 index 0000000000..674ae0a7f3 Binary files /dev/null and b/test/desktop_sikuli/views/images/base_view/next_button.png differ diff --git a/test/desktop_sikuli/views/images/base_view/ok_button.png b/test/desktop_sikuli/views/images/base_view/ok_button.png new file mode 100644 index 0000000000..f9347ae812 Binary files /dev/null and b/test/desktop_sikuli/views/images/base_view/ok_button.png differ diff --git a/test/desktop_sikuli/views/images/base_view/yes_button.png b/test/desktop_sikuli/views/images/base_view/yes_button.png new file mode 100644 index 0000000000..f21252e6ad Binary files /dev/null and b/test/desktop_sikuli/views/images/base_view/yes_button.png differ diff --git a/test/desktop_sikuli/views/images/profile_view/ok_continue_button.png b/test/desktop_sikuli/views/images/profile_view/ok_continue_button.png new file mode 100644 index 0000000000..17dea7db58 Binary files /dev/null and b/test/desktop_sikuli/views/images/profile_view/ok_continue_button.png differ diff --git a/test/desktop_sikuli/views/images/profile_view/recovery_phrase_word_input.png b/test/desktop_sikuli/views/images/profile_view/recovery_phrase_word_input.png new file mode 100644 index 0000000000..8eeb703334 Binary files /dev/null and b/test/desktop_sikuli/views/images/profile_view/recovery_phrase_word_input.png differ diff --git a/test/desktop_sikuli/views/images/profile_view/share_my_code_button.png b/test/desktop_sikuli/views/images/profile_view/share_my_code_button.png deleted file mode 100644 index 02043dbb9c..0000000000 Binary files a/test/desktop_sikuli/views/images/profile_view/share_my_code_button.png and /dev/null differ diff --git a/test/desktop_sikuli/views/images/sign_in_view/i_have_account.png b/test/desktop_sikuli/views/images/sign_in_view/i_have_account.png index dcfbe6acca..c72cbf97fd 100644 Binary files a/test/desktop_sikuli/views/images/sign_in_view/i_have_account.png and b/test/desktop_sikuli/views/images/sign_in_view/i_have_account.png differ diff --git a/test/desktop_sikuli/views/images/sign_in_view/privacy_policy_button.png b/test/desktop_sikuli/views/images/sign_in_view/privacy_policy_button.png deleted file mode 100644 index d773fcbcad..0000000000 Binary files a/test/desktop_sikuli/views/images/sign_in_view/privacy_policy_button.png and /dev/null differ diff --git a/test/desktop_sikuli/views/images/sign_in_view/username_input.png b/test/desktop_sikuli/views/images/sign_in_view/username_input.png index 4cad670ab4..664feedee0 100644 Binary files a/test/desktop_sikuli/views/images/sign_in_view/username_input.png and b/test/desktop_sikuli/views/images/sign_in_view/username_input.png differ diff --git a/test/desktop_sikuli/views/profile_view.py b/test/desktop_sikuli/views/profile_view.py index 8e3cac51a7..0f393bb00a 100644 --- a/test/desktop_sikuli/views/profile_view.py +++ b/test/desktop_sikuli/views/profile_view.py @@ -1,4 +1,8 @@ import os +# import pytesseract +import re +# from PIL import Image +from subprocess import check_output try: import org.sikuli.script.SikulixForJython @@ -6,7 +10,7 @@ try: except Exception: pass -from views.base_element import BaseElement, TextElement +from views.base_element import BaseElement, TextElement, InputField from views.base_view import BaseView IMAGES_PATH = os.path.join(os.path.dirname(__file__), 'images/profile_view') @@ -27,9 +31,12 @@ class MailServerElement(TextElement): class ProfileView(BaseView): def __init__(self): super(ProfileView, self).__init__() - self.share_my_code_button = BaseElement(IMAGES_PATH + '/share_my_code_button.png') + self.share_my_code_button = TextElement('Share my contact code') self.copy_code_button = BaseElement(IMAGES_PATH + '/copy_code_button.png') self.log_out_button = BaseElement(IMAGES_PATH + '/log_out_button.png') + self.back_up_recovery_phrase_button = TextElement('Backup your recovery') + self.ok_continue_button = BaseElement(IMAGES_PATH + '/ok_continue_button.png') + self.recovery_phrase_word_input = InputField(IMAGES_PATH + '/recovery_phrase_word_input.png') def get_mail_servers_list(self): server_names = [] @@ -40,3 +47,21 @@ class ProfileView(BaseView): def get_mail_server(self, name): return MailServerElement(name) + + def get_recovery_phrase(self): + self.share_my_code_button.find_element() + reg = Region(370, 130, 600, 240) + current_text = reg.text().encode('ascii', 'ignore') + phrase_list = re.findall(r"[\w']+", current_text.replace('1O', '10')) + phrase_dict = dict() + for i in range(len(phrase_list) - 1): + if phrase_list[i].isdigit(): + phrase_dict[phrase_list[i]] = phrase_list[i + 1] + return phrase_dict + + def get_recovery_phrase_word_number(self): + image_name = 'recovery.png' + # check_output(['import', '-window', 'root', image_name]) + # text = pytesseract.image_to_string(Image.open(image_name)) + # os.remove(image_name) + # return re.findall('#(.*)\n', text)[0] diff --git a/test/desktop_sikuli/views/sign_in_view.py b/test/desktop_sikuli/views/sign_in_view.py index ad341c8764..a42da150cd 100644 --- a/test/desktop_sikuli/views/sign_in_view.py +++ b/test/desktop_sikuli/views/sign_in_view.py @@ -4,8 +4,8 @@ try: except Exception: pass import os -import pytest -from views.base_element import BaseElement, InputField +import logging +from views.base_element import BaseElement, InputField, TextElement from views.base_view import BaseView from views.home_view import HomeView @@ -25,17 +25,27 @@ class CreateAccountButton(BaseElement): super(CreateAccountButton, self).find_element(log=log) +class UserNameInput(InputField): + def __init__(self): + super(UserNameInput, self).__init__(IMAGES_PATH + '/username_input.png') + + def input_value(self, value): + logging.info("%s field: set value '%s'" % (self.name, value)) + import time + time.sleep(10) + type(value) + + class SignInView(BaseView): def __init__(self): super(SignInView, self).__init__() self.create_account_button = CreateAccountButton() - self.i_have_account_button = BaseElement(IMAGES_PATH + '/i_have_account.png') + self.i_have_account_button = TextElement('I already have an account') self.other_accounts_button = BaseElement(IMAGES_PATH + '/other_accounts.png') - self.privacy_policy_button = BaseElement(IMAGES_PATH + 'privacy_policy_button.png') + self.privacy_policy_button = TextElement('Privacy Policy') self.create_password_input = InputField(IMAGES_PATH + '/create_password_input.png') self.confirm_password_input = InputField(IMAGES_PATH + '/confirm_password_input.png') - self.username_input = InputField(IMAGES_PATH + '/username_input.png') - self.next_button = BaseElement(IMAGES_PATH + '/next_button.png') + self.username_input = UserNameInput() self.recovery_phrase_input = InputField(IMAGES_PATH + '/recovery_phrase_input.png') self.recover_password_input = InputField(IMAGES_PATH + '/recover_password_input.png') self.sign_in_button = BaseElement(IMAGES_PATH + '/sign_in_button.png') @@ -50,12 +60,14 @@ class SignInView(BaseView): self.username_input.input_value(username) self.next_button.click() self.home_button.find_element() + self.ok_button.click() return HomeView() - def recover_access(self, passphrase): + def recover_access(self, passphrase, password='qwerty'): self.i_have_account_button.click() self.recovery_phrase_input.send_keys(passphrase) - self.recover_password_input.send_keys('123456') + self.recover_password_input.send_keys(password) self.sign_in_button.click() self.home_button.find_element() + self.ok_button.click() return HomeView()