diff --git a/qwe.sh b/qwe.sh new file mode 100644 index 000000000..e69de29bb diff --git a/test/ui-pytest/configs/__init__.py b/test/ui-pytest/configs/__init__.py index f68fc5504..df885a504 100644 --- a/test/ui-pytest/configs/__init__.py +++ b/test/ui-pytest/configs/__init__.py @@ -19,3 +19,7 @@ if APP_DIR is None: if system.IS_WIN and 'bin' not in APP_DIR: exit('Please use launcher from "bin" folder in "APP_DIR"') APP_DIR = SystemPath(APP_DIR) + +# Application will be stuck after the first test execution if set to False +# We need to investigate more time on it. +ATTACH_MODE = True diff --git a/test/ui-pytest/configs/_local.py.ci b/test/ui-pytest/configs/_local.py.ci index d20174297..af3374a68 100644 --- a/test/ui-pytest/configs/_local.py.ci +++ b/test/ui-pytest/configs/_local.py.ci @@ -1,5 +1,4 @@ import os LOCAL_RUN = False -ATTACH_MODE = False APP_DIR = os.getenv('APP_DIR') diff --git a/test/ui-pytest/configs/_local.py.default b/test/ui-pytest/configs/_local.py.default index 04950e900..13b3a3ec2 100644 --- a/test/ui-pytest/configs/_local.py.default +++ b/test/ui-pytest/configs/_local.py.default @@ -1,3 +1,2 @@ LOCAL_RUN = True -ATTACH_MODE = True APP_DIR = None diff --git a/test/ui-pytest/conftest.py b/test/ui-pytest/conftest.py index 17bb52680..14fa7af63 100644 --- a/test/ui-pytest/conftest.py +++ b/test/ui-pytest/conftest.py @@ -6,6 +6,7 @@ import pytest from PIL import ImageGrab import configs +import driver from driver.aut import AUT from scripts.utils.system_path import SystemPath from tests.fixtures.path import generate_test_info @@ -58,6 +59,7 @@ def pytest_exception_interact(node): name='Screenshot on fail', body=screenshot.read_bytes(), attachment_type=allure.attachment_type.PNG) + driver.context.detach() AUT().stop() except Exception as ex: _logger.debug(ex) diff --git a/test/ui-pytest/driver/aut.py b/test/ui-pytest/driver/aut.py index 96c19d329..521d58b67 100644 --- a/test/ui-pytest/driver/aut.py +++ b/test/ui-pytest/driver/aut.py @@ -49,8 +49,11 @@ class AUT: return self @allure.step('Close application by process name') - def stop(self, verify: bool = True): - local_system.kill_process_by_name(self.process_name, verify=verify) + def stop(self): + if configs.LOCAL_RUN: + local_system.kill_process_by_port(self.port) + else: + local_system.kill_process_by_name(self.process_name) @allure.step("Start application") def launch(self, *args) -> 'AUT': diff --git a/test/ui-pytest/driver/server.py b/test/ui-pytest/driver/server.py index 9d8dbc9da..a5eb84cde 100644 --- a/test/ui-pytest/driver/server.py +++ b/test/ui-pytest/driver/server.py @@ -1,3 +1,5 @@ +import logging +import time import typing import configs.testpath @@ -5,6 +7,8 @@ from scripts.utils import local_system _PROCESS_NAME = '_squishserver' +_logger = logging.getLogger(__name__) + class SquishServer: @@ -22,19 +26,27 @@ class SquishServer: cmd = [ f'"{self.path}"', '--configfile', str(self.config), - '--verbose', f'--host={self.host}', f'--port={self.port}', ] local_system.execute(cmd) try: local_system.wait_for_started(_PROCESS_NAME) - except AssertionError: + except AssertionError as err: + _logger.info(err) local_system.execute(cmd, check=True) - @classmethod - def stop(cls, attempt: int = 2): - local_system.kill_process_by_name(_PROCESS_NAME, verify=False) + def stop(self, attempt: int = 2): + local_system.run([self.path, '--stop']) + try: + local_system.wait_for_close(_PROCESS_NAME, 2) + except AssertionError as err: + _logger.debug(err) + if attempt: + time.sleep(1) + self.stop(attempt-1) + else: + raise err # https://doc-snapshots.qt.io/squish/cli-squishserver.html def configuring(self, action: str, options: typing.Union[int, str, list]): diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_onboarding.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_onboarding.png new file mode 100644 index 000000000..156de157d Binary files /dev/null and b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_onboarding.png differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_profile.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_profile.png new file mode 100644 index 000000000..6b8b411ff Binary files /dev/null and b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/jpeg_profile.png differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/mac/user_image_profile.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/mac/user_image_profile.png deleted file mode 100644 index 2532bfc23..000000000 Binary files a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/mac/user_image_profile.png and /dev/null differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_onboarding.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_onboarding.png new file mode 100644 index 000000000..6f75463cd Binary files /dev/null and b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_onboarding.png differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_profile.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_profile.png new file mode 100644 index 000000000..81449d877 Binary files /dev/null and b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/png_profile.png differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_onboarding.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_onboarding.png deleted file mode 100644 index 987bb6c7e..000000000 Binary files a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_onboarding.png and /dev/null differ diff --git a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_profile.png b/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_profile.png deleted file mode 100644 index 7be1907cf..000000000 Binary files a/test/ui-pytest/ext/vp/test_onboarding/test_generate_new_keys/user_image_profile.png and /dev/null differ diff --git a/test/ui-pytest/scripts/utils/local_system.py b/test/ui-pytest/scripts/utils/local_system.py index ff0f40260..3b50efa56 100644 --- a/test/ui-pytest/scripts/utils/local_system.py +++ b/test/ui-pytest/scripts/utils/local_system.py @@ -33,6 +33,35 @@ def find_process_by_name(process_name: str): return processes +@allure.step('Find process by pid') +def find_process_by_pid(pid): + for proc in psutil.process_iter(): + try: + if proc.pid == pid: + return process_info( + proc.pid, + proc.name(), + datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f") + ) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + + +@allure.step('Find process by port') +def find_process_by_port(port: int): + for proc in psutil.process_iter(): + try: + for conns in proc.connections(kind='inet'): + if conns.laddr.port == port: + return process_info( + proc.pid, + proc.name(), + datetime.fromtimestamp(proc.create_time()).strftime("%H:%M:%S.%f") + ) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + + @allure.step('Kill process by name') def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: int = 10): _logger.info(f'Closing process: {process_name}') @@ -46,8 +75,22 @@ def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: in wait_for_close(process_name, timeout_sec) +@allure.step('Kill process by PID') +def kill_process_by_pid(pid, verify: bool = True, timeout_sec: int = 10): + os.kill(pid, signal.SIGILL if IS_WIN else signal.SIGKILL) + if verify: + wait_for_close(pid=pid, timeout_sec=timeout_sec) + + +@allure.step('Kill process by port') +def kill_process_by_port(port: int): + proc = find_process_by_port(port) + if proc is not None and proc.pid: + kill_process_by_pid(proc.pid) + + @allure.step('Wait for process start') -def wait_for_started(process_name: str, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC): +def wait_for_started(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC): started_at = time.monotonic() while True: process = find_process_by_name(process_name) @@ -60,11 +103,19 @@ def wait_for_started(process_name: str, timeout_sec: int = configs.timeouts.PROC @allure.step('Wait for process close') -def wait_for_close(process_name: str, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC): +def wait_for_close(process_name: str = None, timeout_sec: int = configs.timeouts.PROCESS_TIMEOUT_SEC, pid=None): started_at = time.monotonic() while True: - if not find_process_by_name(process_name): - break + if process_name is not None: + process = find_process_by_name(process_name) + if not process: + break + elif pid is not None: + process = find_process_by_pid(pid) + if process is None: + break + else: + raise RuntimeError('Set process name or PID to find process') time.sleep(1) assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name}' _logger.info(f'Process closed: {process_name}') @@ -112,3 +163,4 @@ def run( process = subprocess.run(command, shell=shell, stderr=stderr, stdout=stdout, timeout=timeout_sec) if check and process.returncode != 0: raise subprocess.CalledProcessError(process.returncode, command, process.stdout, process.stderr) + _logger.debug(f'stdout: {process.stdout}') diff --git a/test/ui-pytest/tests/test_onboarding.py b/test/ui-pytest/tests/test_onboarding.py index 0fe0ab806..6850efa18 100755 --- a/test/ui-pytest/tests/test_onboarding.py +++ b/test/ui-pytest/tests/test_onboarding.py @@ -1,4 +1,5 @@ import logging + import allure import pytest from allure import step @@ -18,13 +19,15 @@ pytestmark = allure.suite("Onboarding") @allure.testcase('https://ethstatus.testrail.net/index.php?/cases/view/703421', 'Generate new keys') @pytest.mark.case(703421) -@pytest.mark.parametrize('user_name, password, user_image', [ - pytest.param('Test-User _1', '*P@ssw0rd*', None), - pytest.param('_1Test-User', '*P@ssw0rd*', 'tv_signal.jpeg', marks=pytest.mark.smoke), - pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png'), +@pytest.mark.parametrize('user_name, password, user_image, zoom, shift', [ + pytest.param('Test-User _1', '*P@ssw0rd*', None, None, None), + pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png', 5, shift_image(0, 0, 0, 0)), + pytest.param('_1Test-User', '*P@ssw0rd*', 'tv_signal.jpeg', 5, shift_image(0, 1000, 1000, 0), + marks=pytest.mark.smoke), ]) -def test_generate_new_keys(main_window, user_name, password, user_image: str): +def test_generate_new_keys(main_window, user_name, password, user_image: str, zoom, shift): with step('Open Generate new keys view'): + if configs.system.IS_MAC: AllowNotificationsView().wait_until_appears().allow() BeforeStartedPopUp().get_started() @@ -32,17 +35,17 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str): keys_screen = wellcome_screen.get_keys() with step(f'Setup profile with name: {user_name} and image: {user_image}'): + profile_view = keys_screen.generate_new_keys() profile_view.set_display_name(user_name) if user_image is not None: profile_picture_popup = profile_view.set_user_image(configs.testpath.TEST_FILES / user_image) - profile_picture_popup.make_profile_picture(zoom=5, shift=shift_image(0, 200, 200, 0)) + profile_picture_popup.make_profile_picture(zoom=zoom, shift=shift) assert not profile_view.error_message - with step('Open Profile details view'): - details_view = profile_view.next() + with step('Open Profile details view and verify user info'): - with step('Verify Profile details'): + details_view = profile_view.next() if user_image is None: assert not details_view.is_user_image_background_white() assert driver.waitFor( @@ -52,14 +55,15 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str): else: image.compare( details_view.cropped_profile_image, - configs.testpath.TEST_VP / f'user_image_onboarding.png', + f'{user_image.split(".")[1]}_onboarding.png', + threshold=0.9 ) - chat_key = details_view.chat_key emoji_hash = details_view.emoji_hash assert details_view.is_identicon_ring_visible - with step('Finalize onboarding and prepare main screen'): + with step('Finalize onboarding and open main screen'): + create_password_view = details_view.next() assert not create_password_view.is_create_password_button_enabled confirm_password_view = create_password_view.create_password(password) @@ -69,7 +73,8 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str): SplashScreen().wait_until_appears().wait_until_hidden() WelcomeStatusPopup().confirm() - with step('Open User Canvas and verify profile'): + with step('Open User Canvas and verify user info'): + user_canvas = main_window.left_panel.open_user_canvas() assert user_canvas.user_name == user_name if user_image is None: @@ -78,11 +83,12 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str): configs.timeouts.UI_LOAD_TIMEOUT_MSEC ) - with step('Open Profile popup and verify profile'): + with step('Open Profile popup and verify user info'): + profile_popup = user_canvas.open_profile_popup() assert profile_popup.user_name == user_name assert profile_popup.chat_key == chat_key - assert profile_popup.emoji_hash.compare(emoji_hash.view) + assert profile_popup.emoji_hash.compare(emoji_hash.view, threshold=0.9) if user_image is None: assert driver.waitFor( lambda: profile_popup.is_user_image_contains(user_name[:2]), @@ -91,5 +97,6 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str): else: image.compare( profile_popup.cropped_profile_image, - 'user_image_profile.png', + f'{user_image.split(".")[1]}_profile.png', + threshold=0.9 )