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:
|
||||
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
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import os
|
||||
|
||||
LOCAL_RUN = False
|
||||
ATTACH_MODE = False
|
||||
APP_DIR = os.getenv('APP_DIR')
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
LOCAL_RUN = True
|
||||
ATTACH_MODE = True
|
||||
APP_DIR = None
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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]):
|
||||
|
|
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
|
||||
|
||||
|
||||
@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}')
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|