test(Onboarding) Skip closing test app (#11837)
- Closing all app instances by port for local run and by process name for CI - Threshold for emoji hash decreased in image comparison - Verification point for cropped user icon added
|
@ -19,3 +19,7 @@ if APP_DIR is None:
|
||||||
if system.IS_WIN and 'bin' not in APP_DIR:
|
if system.IS_WIN and 'bin' not in APP_DIR:
|
||||||
exit('Please use launcher from "bin" folder in "APP_DIR"')
|
exit('Please use launcher from "bin" folder in "APP_DIR"')
|
||||||
APP_DIR = SystemPath(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
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
LOCAL_RUN = False
|
LOCAL_RUN = False
|
||||||
ATTACH_MODE = False
|
|
||||||
APP_DIR = os.getenv('APP_DIR')
|
APP_DIR = os.getenv('APP_DIR')
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
LOCAL_RUN = True
|
LOCAL_RUN = True
|
||||||
ATTACH_MODE = True
|
|
||||||
APP_DIR = None
|
APP_DIR = None
|
||||||
|
|
|
@ -6,6 +6,7 @@ import pytest
|
||||||
from PIL import ImageGrab
|
from PIL import ImageGrab
|
||||||
|
|
||||||
import configs
|
import configs
|
||||||
|
import driver
|
||||||
from driver.aut import AUT
|
from driver.aut import AUT
|
||||||
from scripts.utils.system_path import SystemPath
|
from scripts.utils.system_path import SystemPath
|
||||||
from tests.fixtures.path import generate_test_info
|
from tests.fixtures.path import generate_test_info
|
||||||
|
@ -58,6 +59,7 @@ def pytest_exception_interact(node):
|
||||||
name='Screenshot on fail',
|
name='Screenshot on fail',
|
||||||
body=screenshot.read_bytes(),
|
body=screenshot.read_bytes(),
|
||||||
attachment_type=allure.attachment_type.PNG)
|
attachment_type=allure.attachment_type.PNG)
|
||||||
|
driver.context.detach()
|
||||||
AUT().stop()
|
AUT().stop()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
_logger.debug(ex)
|
_logger.debug(ex)
|
||||||
|
|
|
@ -49,8 +49,11 @@ class AUT:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@allure.step('Close application by process name')
|
@allure.step('Close application by process name')
|
||||||
def stop(self, verify: bool = True):
|
def stop(self):
|
||||||
local_system.kill_process_by_name(self.process_name, verify=verify)
|
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")
|
@allure.step("Start application")
|
||||||
def launch(self, *args) -> 'AUT':
|
def launch(self, *args) -> 'AUT':
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import configs.testpath
|
import configs.testpath
|
||||||
|
@ -5,6 +7,8 @@ from scripts.utils import local_system
|
||||||
|
|
||||||
_PROCESS_NAME = '_squishserver'
|
_PROCESS_NAME = '_squishserver'
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SquishServer:
|
class SquishServer:
|
||||||
|
|
||||||
|
@ -22,19 +26,27 @@ class SquishServer:
|
||||||
cmd = [
|
cmd = [
|
||||||
f'"{self.path}"',
|
f'"{self.path}"',
|
||||||
'--configfile', str(self.config),
|
'--configfile', str(self.config),
|
||||||
'--verbose',
|
|
||||||
f'--host={self.host}',
|
f'--host={self.host}',
|
||||||
f'--port={self.port}',
|
f'--port={self.port}',
|
||||||
]
|
]
|
||||||
local_system.execute(cmd)
|
local_system.execute(cmd)
|
||||||
try:
|
try:
|
||||||
local_system.wait_for_started(_PROCESS_NAME)
|
local_system.wait_for_started(_PROCESS_NAME)
|
||||||
except AssertionError:
|
except AssertionError as err:
|
||||||
|
_logger.info(err)
|
||||||
local_system.execute(cmd, check=True)
|
local_system.execute(cmd, check=True)
|
||||||
|
|
||||||
@classmethod
|
def stop(self, attempt: int = 2):
|
||||||
def stop(cls, attempt: int = 2):
|
local_system.run([self.path, '--stop'])
|
||||||
local_system.kill_process_by_name(_PROCESS_NAME, verify=False)
|
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
|
# https://doc-snapshots.qt.io/squish/cli-squishserver.html
|
||||||
def configuring(self, action: str, options: typing.Union[int, str, list]):
|
def configuring(self, action: str, options: typing.Union[int, str, list]):
|
||||||
|
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB |
|
@ -33,6 +33,35 @@ def find_process_by_name(process_name: str):
|
||||||
return processes
|
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')
|
@allure.step('Kill process by name')
|
||||||
def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: int = 10):
|
def kill_process_by_name(process_name: str, verify: bool = True, timeout_sec: int = 10):
|
||||||
_logger.info(f'Closing process: {process_name}')
|
_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)
|
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')
|
@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()
|
started_at = time.monotonic()
|
||||||
while True:
|
while True:
|
||||||
process = find_process_by_name(process_name)
|
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')
|
@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()
|
started_at = time.monotonic()
|
||||||
while True:
|
while True:
|
||||||
if not find_process_by_name(process_name):
|
if process_name is not None:
|
||||||
|
process = find_process_by_name(process_name)
|
||||||
|
if not process:
|
||||||
break
|
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)
|
time.sleep(1)
|
||||||
assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name}'
|
assert time.monotonic() - started_at < timeout_sec, f'Close process error: {process_name}'
|
||||||
_logger.info(f'Process closed: {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)
|
process = subprocess.run(command, shell=shell, stderr=stderr, stdout=stdout, timeout=timeout_sec)
|
||||||
if check and process.returncode != 0:
|
if check and process.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(process.returncode, command, process.stdout, process.stderr)
|
raise subprocess.CalledProcessError(process.returncode, command, process.stdout, process.stderr)
|
||||||
|
_logger.debug(f'stdout: {process.stdout}')
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import allure
|
import allure
|
||||||
import pytest
|
import pytest
|
||||||
from allure import step
|
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')
|
@allure.testcase('https://ethstatus.testrail.net/index.php?/cases/view/703421', 'Generate new keys')
|
||||||
@pytest.mark.case(703421)
|
@pytest.mark.case(703421)
|
||||||
@pytest.mark.parametrize('user_name, password, user_image', [
|
@pytest.mark.parametrize('user_name, password, user_image, zoom, shift', [
|
||||||
pytest.param('Test-User _1', '*P@ssw0rd*', None),
|
pytest.param('Test-User _1', '*P@ssw0rd*', None, None, None),
|
||||||
pytest.param('_1Test-User', '*P@ssw0rd*', 'tv_signal.jpeg', marks=pytest.mark.smoke),
|
pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png', 5, shift_image(0, 0, 0, 0)),
|
||||||
pytest.param('Test-User', '*P@ssw0rd*', 'tv_signal.png'),
|
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'):
|
with step('Open Generate new keys view'):
|
||||||
|
|
||||||
if configs.system.IS_MAC:
|
if configs.system.IS_MAC:
|
||||||
AllowNotificationsView().wait_until_appears().allow()
|
AllowNotificationsView().wait_until_appears().allow()
|
||||||
BeforeStartedPopUp().get_started()
|
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()
|
keys_screen = wellcome_screen.get_keys()
|
||||||
|
|
||||||
with step(f'Setup profile with name: {user_name} and image: {user_image}'):
|
with step(f'Setup profile with name: {user_name} and image: {user_image}'):
|
||||||
|
|
||||||
profile_view = keys_screen.generate_new_keys()
|
profile_view = keys_screen.generate_new_keys()
|
||||||
profile_view.set_display_name(user_name)
|
profile_view.set_display_name(user_name)
|
||||||
if user_image is not None:
|
if user_image is not None:
|
||||||
profile_picture_popup = profile_view.set_user_image(configs.testpath.TEST_FILES / user_image)
|
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
|
assert not profile_view.error_message
|
||||||
|
|
||||||
with step('Open Profile details view'):
|
with step('Open Profile details view and verify user info'):
|
||||||
details_view = profile_view.next()
|
|
||||||
|
|
||||||
with step('Verify Profile details'):
|
details_view = profile_view.next()
|
||||||
if user_image is None:
|
if user_image is None:
|
||||||
assert not details_view.is_user_image_background_white()
|
assert not details_view.is_user_image_background_white()
|
||||||
assert driver.waitFor(
|
assert driver.waitFor(
|
||||||
|
@ -52,14 +55,15 @@ def test_generate_new_keys(main_window, user_name, password, user_image: str):
|
||||||
else:
|
else:
|
||||||
image.compare(
|
image.compare(
|
||||||
details_view.cropped_profile_image,
|
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
|
chat_key = details_view.chat_key
|
||||||
emoji_hash = details_view.emoji_hash
|
emoji_hash = details_view.emoji_hash
|
||||||
assert details_view.is_identicon_ring_visible
|
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()
|
create_password_view = details_view.next()
|
||||||
assert not create_password_view.is_create_password_button_enabled
|
assert not create_password_view.is_create_password_button_enabled
|
||||||
confirm_password_view = create_password_view.create_password(password)
|
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()
|
SplashScreen().wait_until_appears().wait_until_hidden()
|
||||||
WelcomeStatusPopup().confirm()
|
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()
|
user_canvas = main_window.left_panel.open_user_canvas()
|
||||||
assert user_canvas.user_name == user_name
|
assert user_canvas.user_name == user_name
|
||||||
if user_image is None:
|
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
|
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()
|
profile_popup = user_canvas.open_profile_popup()
|
||||||
assert profile_popup.user_name == user_name
|
assert profile_popup.user_name == user_name
|
||||||
assert profile_popup.chat_key == chat_key
|
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:
|
if user_image is None:
|
||||||
assert driver.waitFor(
|
assert driver.waitFor(
|
||||||
lambda: profile_popup.is_user_image_contains(user_name[:2]),
|
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:
|
else:
|
||||||
image.compare(
|
image.compare(
|
||||||
profile_popup.cropped_profile_image,
|
profile_popup.cropped_profile_image,
|
||||||
'user_image_profile.png',
|
f'{user_image.split(".")[1]}_profile.png',
|
||||||
|
threshold=0.9
|
||||||
)
|
)
|
||||||
|
|