Initial commit

master
Radislav 2025-07-04 09:44:15 +03:00
commit 51123880b9
51 changed files with 2422 additions and 0 deletions

3
.env Normal file
View File

@ -0,0 +1,3 @@
ENV=test
AUTH_LOGIN = admin
AUTH_PASSWORD = admin

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

12
Locators/base_page.py Normal file
View File

@ -0,0 +1,12 @@
class BasePageLocators():
TABLE_HEADERS_CELLS = "//thead[contains(@class,'scrolltable__header')]/tr[contains(@class,'scrolltable__row')]/th"
TABLE_BODY = "//tbody[contains(@class,'scrolltable__body')]/tr[contains(@class,'scrolltable__row')]"
ALERT_WINDOW = "//div[@role='alert']"
ALERT_WINDOW_TEXT_ERROR = "//div[@class='v-alert error']/div"
ALERT_WINDOW_TEXT_SUCCESS = "//div[@class='v-alert success']/div"
ALERT_WINDOW_TEXT_INFO = "//div[@class='v-alert info']/div"
ALERT_WINDOW_TEXT_WARNING = "//div[@class='v-alert warning']/div"
TOOLTIP = "//div[contains(@class,'v-tooltip__content menuable__content__active')]"

View File

@ -0,0 +1,20 @@
class ConfigurationPageLocators():
CONFIG_NAVIGATION_PANEL = "//ul/li[3]/div[@class='v-expansion-panel__body']"
CONFIG_NAVIGATION_PANEL_USER_BUTTON = "//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')]"
CONFIG_NAVIGATION_PANEL_NOTIFICATION_BUTTON = "//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][2]"
CONFIG_NAVIGATION_PANEL_MAINTENANCE_BUTTON = "//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][3]"
CONFIG_NAVIGATION_PANEL_ZTP_BUTTON = "//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][4]"
MAINTENANCE_NAVIGATION_PANEL = \
"//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][3]/div[@class='v-treeview-node__children']"
MAINTENANCE_NAVIGATION_PANEL_SESSION_BUTTON = \
"//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][3]/div[@class='v-treeview-node__children']//div[contains(@class,'v-treeview-node--click')]"
MAINTENANCE_NAVIGATION_PANEL_SERVICE_STATUS_BUTTON = \
"//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][3]/div[@class='v-treeview-node__children']//div[contains(@class,'v-treeview-node--click')][2]"
MAINTENANCE_NAVIGATION_PANEL_LICENSING_BUTTON = \
"//ul/li[3]/div[@class='v-expansion-panel__body']//div[contains(@class,'v-treeview-node--click')][3]/div[@class='v-treeview-node__children']//div[contains(@class,'v-treeview-node--click')][3]"
WORK_AREA_TITLE = "//div[@class ='v-toolbar__title']/span"
WORK_AREA_TABLE = "//div[@class='scrollarea__body']/div/div/div/table"

5
Locators/license_tab.py Normal file
View File

@ -0,0 +1,5 @@
class LicenseTabLocators:
JSON_ELEMENT = "//div[@class='jv-node']"
LICENSE_TITLE = "//span[@class='title']"
LICENSE_TITLE_DEVICE_ID = "//span[@class='title text_select']"
LICENSE_INPUT_FORM_TEXTAREA = "//div[contains(@class,'v-input')]/div[@class='v-input__control']//div[@class='v-text-field__slot']/textarea"

4
Locators/login.py Normal file
View File

@ -0,0 +1,4 @@
class LoginPageLocators:
USERNAME_INPUT = "//input[@type='text']"
PASSWORD_INPUT = "//input[@type='password']"
LOGIN_BTN = "//button[@type='button']"

14
Locators/main_page.py Normal file
View File

@ -0,0 +1,14 @@
class MainPageLocators:
NAVIGATION_PANEL = "//ul"
NAVIGATION_PANEL_DASHBOARD_BUTTON = "//ul/li[1]/div"
NAVIGATION_PANEL_TOPOLOGY_BUTTON = "//ul/li[2]/div"
NAVIGATION_PANEL_CONFIGURATION_BUTTON = "//ul/li[3]/div"
NAVIGATION_PANEL_DASHBOARD_BUTTON_HEADER = "//ul/li[1]"
NAVIGATION_PANEL_TOPOLOGY_BUTTON_HEADER = "//ul/li[2]"
NAVIGATION_PANEL_CONFIGURATION_BUTTON_HEADER = "//ul/li[3]"
CURRENT_USER_BUTTON = "#app > div.application--wrap > div > div.layout.white > nav > div > div:nth-child(3) > div > div > div > button:nth-child(3)"
CURRENT_USER_WINDOW = "//div[@class='v-card__text']"

View File

@ -0,0 +1,31 @@
class SessionLocators():
# Область работы
#TABLE_BODY = "//div[contains(@class, 'scrollarea__body')]//table[@class='scrolltable__container']"
TABLE_BODY = "//div[@class='scrollarea__body']/div/div/div/table"
TEXT_TITLE = "//div[@class ='v-toolbar__title']/span"
TABLE_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//table[@class='scrolltable__container']]" # Контейнер со скроллом
TABLE_ROWS = f"{TABLE_SCROLL_CONTAINER}//table/tbody/tr"
#TABLE_SCROLL_CONTAINER = "div.layout.white.column.fill-height"
# Ячейки таблицы
TABLE_HEADER_CELL_SESSION_ID = "//div[@class='scrollarea__body']//td[1]/div/div"
TABLE_HEADER_CELL_USER_ID = "//div[@class='scrollarea__body']//td[2]/div/div"
TABLE_HEADER_CELL_LIFETIME = "//div[@class='scrollarea__body']//td[3]//div/div"
TABLE_HEADER_CELL_ROLE = "//div[@class='scrollarea__body']//td[4]//div/div"
TABLE_HEADER_CELL_ADDRESS = "//div[@class='scrollarea__body']//td[5]//div/div"
# Кнопки
BUTTON_DELETE_SESSION = "button.v-btn--icon svg[fill='#4caf50']" # "//div[@class='scrollarea__body']//td[5]//button"
# Модальное окно
MODAL_WINDOW = "div.v-dialog--active" # Основное модальное окно
MODAL_TITLE = "//*[@id='app']/div[2]/div/div[2]/div[1]" # Заголовк модального окна
#MODAL_SCROLL_CONTAINER_HORIZONTAL = "div.v-dialog--active div.v-card__text" # Горизонтальный скролл
MODAL_CLOSE_BUTTON = "//*[@id='app']/div[2]/div/div[1]" # Кнопка закрытия "Х"
MODAL_CANCEL_BUTTON = "div.v-dialog--active button:not(.red--text)" # Кнопка "Отмена"
MODAL_DELETE_BUTTON = "div.v-dialog--active button.red--text" # Кнопка "Удалить"
#SUCCESS_MESSAGE = "селектор сообщения об успешном удалении" # опционально

14
Locators/users_tab.py Normal file
View File

@ -0,0 +1,14 @@
class UsersTabLocators:
TOOLBAR_EDIT_BUTTON = "xpath=(.//*[normalize-space(text()) and normalize-space(.)='Пользователи'])[2]/following::*[name()='svg'][1]"
TOOLBAR_ADD_USER_BUTTON = "xpath=(.//*[normalize-space(text()) and normalize-space(.)='Пользователи'])[2]/following::*[name()='svg'][1]"
TOOLBAR_CLOSE_BUTTON = "xpath=(.//*[normalize-space(text()) and normalize-space(.)='Пользователи'])[2]/following::*[name()='svg'][2]"
USER_DATA_WORK_AREA_TITLE = "//div[@class ='v-toolbar__title']/span[contains(@class,'body-2')]"
ADD_USER_WORK_AREA_CLOSE_BUTTON = "xpath=(.//*[normalize-space(text()) and normalize-space(.)='Добавить нового пользователя'])[1]/following::*[name()='svg'][1]"
USER_DATA_INPUT_FORM = "//form[@class='v-form']"
USER_DATA_INPUT_FORM_ROLES_MENU = "//div[contains(@class, 'menuable__content__active')]"
USER_DATA_INPUT_FORM_NOTIFICATION_LABEL = "//label[contains(@class,'v-label')]/span"
USER_ACTION_CONFIRMATION_DIALOG = "//div[contains(@class, 'v-dialog--active')]//h3"

Binary file not shown.

7
conftest.py Normal file
View File

@ -0,0 +1,7 @@
from dotenv import load_dotenv
load_dotenv()
pytest_plugins = [
'fixtures.pages'
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

107
data/assertions.py Normal file
View File

@ -0,0 +1,107 @@
from playwright.sync_api import Page
from data.environment import host
from playwright.sync_api import expect
from pages.base_page import BasePage
from Locators.base_page import BasePageLocators
import jsondiff
class Assertions(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
def check_URL(self, uri, msg):
expect(self.page).to_have_url(f"{host.get_base_url()}{uri}", timeout=60000), msg
def have_text(self, locator, text: str, msg): #элемент имеет текст
loc = self.page.locator(locator)
expect(loc).to_have_text(text), msg
def have_title(self, locator, title: str, msg):
loc = self.page.locator(locator)
expect(loc).to_have_text(title), msg
def check_presence(self, locator, msg):
loc = self.page.locator(locator)
expect(loc).to_be_visible(visible=True, timeout=12000), msg
def check_button_presence_with_text(self, text, msg):
loc = self.page.get_by_role("button", name=text)
expect(loc).to_be_visible(visible=True, timeout=12000), msg
def check_absence(self, locator, msg):
loc = self.page.locator(locator)
expect(loc).to_be_hidden(timeout=700), msg
def check_absence_after_period(self, locator, period, msg):
loc = self.page.locator(locator)
expect(loc).to_be_hidden(timeout=period), msg
def check_empty_input_area(self, input_area, msg):
expect(input_area).to_be_empty(), msg
def check_menu_item_with_text(self, text, msg):
count = self.page.get_by_role("listitem", name=text).count()
assert count != 1, msg
def check_equals(self, actual, expected, msg):
assert actual == expected, msg
def check_not_equals(self, actual, expected, msg):
assert actual != expected, msg
def check_json_equals(self, actual, expected, msg):
diff = jsondiff.diff(expected, actual, syntax='symmetric')
assert len(diff) == 0, f"{msg}. DIFF is {diff}"
def check_lists_equals(self, actual, expected, msg):
def compare_lists(list1, list2):
if len(list1) != len(list2):
return False
for item1, item2 in zip(list1, list2):
if isinstance(item1, list) and isinstance(item2, list):
if not compare_lists(item1, item2):
return False
elif item1 != item2:
return False
return True
assert compare_lists(actual, expected), msg
def check_url_content(self, uri,msg):
assert f"{uri}" in self.page.url, msg
def check_element_active(self, locator, msg):
element_handle = self.page.wait_for_selector(locator)
class_names = element_handle.get_attribute('class')
assert 'active' in class_names, msg
def check_alert_window_with_text(self, alert_type, text):
self.check_presence(BasePageLocators.ALERT_WINDOW, "No alert window for action notification")
if alert_type == "error":
self.have_text(BasePageLocators.ALERT_WINDOW_TEXT_ERROR, text, \
"Unexpected error message in alert window")
elif alert_type == "success":
self.have_text(BasePageLocators.ALERT_WINDOW_TEXT_SUCCESS, text, \
"Unexpected message about success action in alert window")
elif alert_type == "info":
self.have_text(BasePageLocators.ALERT_WINDOW_TEXT_WARNING, text, \
"Unexpected info message in alert window")
elif alert_type == "warning":
self.have_text(BasePageLocators.ALERT_WINDOW_TEXT_WARNING, text, \
"Unexpected warning message about success action in alert window")
else:
assert False, "Unsupported type of alert window"
self.check_absence_after_period(BasePageLocators.ALERT_WINDOW, 30000, \
"Alert window for action notification should disappear")
def check_tooltip_with_text(self, locator, text):
self.page.locator(locator).hover()
tooltip = self.page.locator(BasePageLocators.TOOLTIP)
assert tooltip.text_content().strip() == text, "Unexpected tooltip text"
def check_confirmation_dialog_with_title(self, locator, title):
loc = self.page.locator(locator)
expect(loc).to_contain_text(title), f"Confirmation dialog window with title {title} is not present"

9
data/constants.py Normal file
View File

@ -0,0 +1,9 @@
import os
class Constants:
try:
login = os.getenv('AUTH_LOGIN')
password = os.getenv('AUTH_PASSWORD')
except KeyError:
print("LOGIN OR PASSWORD WASN'T FOUND")

31
data/environment.py Normal file
View File

@ -0,0 +1,31 @@
import os
class Environment:
TEST = 'test'
DEVELOP = 'develop'
URLS = {
TEST: 'http://192.168.2.76/',
DEVELOP: 'http://192.168.50.69/'
}
def __init__(self):
try:
self.env = os.getenv('ENV')
self.access_token = ""
except KeyError:
self.env = self.TEST
def get_base_url(self):
if self.env in self.URLS:
return self.URLS[self.env]
else:
raise Exception(f"Unknown value of ENV variable {self.env}")
def set_access_token(self, token):
self.token = token
def get_access_token(self):
return self.token
host = Environment()

95
fixtures/pages.py Normal file
View File

@ -0,0 +1,95 @@
import pytest
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright
import os
def pytest_addoption(parser):
"""Пользовательские опции командной строки"""
parser.addoption('--bn', action='store', default="chrome", help="Choose browser: chrome, remote_chrome or firefox")
parser.addoption('--h', action='store', default=False, help='Choose headless: True or False')
parser.addoption('--s', action='store', default={'width': 1520, 'height': 380}, help='Size window: width,height')
# parser.addoption('--s', action='store', default={'width': 1920, 'height': 300}, help='Size window: width,height')
parser.addoption('--slow', action='store', default=200, help='Choose slow_mo for robot action')
parser.addoption('--t', action='store', default=60000, help='Choose timeout')
parser.addoption('--l', action='store', default='ru-RU', help='Choose locale')
# parser.addini('qs_to_api_token', default=os.getenv("QASE_TOKEN"), help='Qase app token')
@pytest.fixture(scope='class')
def browser(request) -> Page:
playwright = sync_playwright().start()
if request.config.getoption("bn") == 'remote_chrome':
browser = get_remote_chrome(playwright, request)
context = get_context(browser, request, 'remote')
page_data = context.new_page()
elif request.config.getoption("bn") == 'firefox':
browser = get_firefox_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
elif request.config.getoption("bn") == 'chrome':
browser = get_chrome_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
else:
browser = get_chrome_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
yield page_data
for context in browser.contexts:
context.close()
browser.close()
playwright.stop()
def get_firefox_browser(playwright, request) -> Browser:
return playwright.firefox.launch(
headless=request.config.getoption("h"),
slow_mo=request.config.getoption("slow"),
)
def get_chrome_browser(playwright, request) -> Browser:
return playwright.chromium.launch(
headless=request.config.getoption("h"),
slow_mo=request.config.getoption("slow"),
args=['--s']
)
def get_remote_chrome(playwright, request) -> Browser:
return playwright.chromium.launch(
headless=True,
slow_mo=request.config.getoption("slow")
)
def get_context(browser, request, start) -> BrowserContext:
if start == 'local':
context = browser.new_context(
# no_viewport=True,
viewport=request.config.getoption('s'),
locale=request.config.getoption('l')
)
context.set_default_timeout(
timeout=request.config.getoption('t')
)
# context.add_cookies([{'url': 'https://example.ru', 'name': 'ab_test', 'value': 'd'}]) добавляем куки, если нужны
return context
elif start == 'remote':
context = browser.new_context(
viewport=request.config.getoption('s'),
locale=request.config.getoption('l')
)
context.set_default_timeout(
timeout=request.config.getoption('t')
)
# context.add_cookies([{'url': 'https://example.ru', 'name': 'ab_test', 'value': 'd'}]) добавляем куки, если нужны
return context
@pytest.fixture(scope="function")
def return_back(browser):
browser.go_back()

93
fixtures/pages.py.or Normal file
View File

@ -0,0 +1,93 @@
import pytest
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright
import os
def pytest_addoption(parser):
"""Пользовательские опции командной строки"""
parser.addoption('--bn', action='store', default="chrome", help="Choose browser: chrome, remote_chrome or firefox")
parser.addoption('--h', action='store', default=False, help='Choose headless: True or False')
parser.addoption('--s', action='store', default={'width': 1920, 'height': 1080}, help='Size window: width,height')
parser.addoption('--slow', action='store', default=200, help='Choose slow_mo for robot action')
parser.addoption('--t', action='store', default=60000, help='Choose timeout')
parser.addoption('--l', action='store', default='ru-RU', help='Choose locale')
# parser.addini('qs_to_api_token', default=os.getenv("QASE_TOKEN"), help='Qase app token')
@pytest.fixture(scope='class')
def browser(request) -> Page:
playwright = sync_playwright().start()
if request.config.getoption("bn") == 'remote_chrome':
browser = get_remote_chrome(playwright, request)
context = get_context(browser, request, 'remote')
page_data = context.new_page()
elif request.config.getoption("bn") == 'firefox':
browser = get_firefox_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
elif request.config.getoption("bn") == 'chrome':
browser = get_chrome_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
else:
browser = get_chrome_browser(playwright, request)
context = get_context(browser, request, 'local')
page_data = context.new_page()
yield page_data
for context in browser.contexts:
context.close()
browser.close()
playwright.stop()
def get_firefox_browser(playwright, request) -> Browser:
return playwright.firefox.launch(
headless=request.config.getoption("h"),
slow_mo=request.config.getoption("slow"),
)
def get_chrome_browser(playwright, request) -> Browser:
return playwright.chromium.launch(
headless=request.config.getoption("h"),
slow_mo=request.config.getoption("slow"),
args=['--start-maximized']
)
def get_remote_chrome(playwright, request) -> Browser:
return playwright.chromium.launch(
headless=True,
slow_mo=request.config.getoption("slow")
)
def get_context(browser, request, start) -> BrowserContext:
if start == 'local':
context = browser.new_context(
no_viewport=True,
locale=request.config.getoption('l')
)
context.set_default_timeout(
timeout=request.config.getoption('t')
)
# context.add_cookies([{'url': 'https://example.ru', 'name': 'ab_test', 'value': 'd'}]) добавляем куки, если нужны
return context
elif start == 'remote':
context = browser.new_context(
viewport=request.config.getoption('s'),
locale=request.config.getoption('l')
)
context.set_default_timeout(
timeout=request.config.getoption('t')
)
# context.add_cookies([{'url': 'https://example.ru', 'name': 'ab_test', 'value': 'd'}]) добавляем куки, если нужны
return context
@pytest.fixture(scope="function")
def return_back(browser):
browser.go_back()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

296
pages/base_page.py Normal file
View File

@ -0,0 +1,296 @@
from playwright.sync_api import Page, TimeoutError, Response, APIRequestContext, expect
from data.environment import host
from Locators.base_page import BasePageLocators
import time
import json
class BasePage:
def __init__(self, page: Page):
self.page = page
self.token = ""
def open(self, uri) -> Response | None:
return self.page.goto(f"{host.get_base_url()}{uri}", wait_until='domcontentloaded')
def get_api_request_context(self) -> APIRequestContext:
return self.page.context.request
#return page url
def current_url(self) -> str:
return self.page.url
# click on element
def click(self, locator: str) -> None:
self.page.click(locator)
# input in field
def input(self, locator: str, data: str) -> None:
self.page.locator(locator).fill(data)
# clear input field
def clear_input(self, locator: str) -> None:
self.page.locator(locator).press('Control+A')
self.page.locator(locator).press('Backspace')
def page_reload(self) ->None:
self.page.reload()
# wait for element
def wait_for_element(self, locator, timeout=12000) -> None:
self.page.wait_for_selector(locator, timeout=timeout)
# wait for all elements
def wait_for_all_elements(self, locator, timeout=5000):
elements = self.page.query_selector_all(locator)
for element in elements:
self.page.wait_for_selector(locator, timeout=timeout)
return elements
# if element presents then ok
def is_element_present(self, locator: str, timeout: int = 5000) -> bool:
try:
self.page.wait_for_selector(locator, timeout=timeout)
except TimeoutError:
return False
return True
# if element is not present then ok
def is_element_NOT_presence(self, locator: str, timeout: int = 5000) -> bool:
try:
self.page.wait_for_selector(locator, timeout=timeout)
except TimeoutError:
return True
return False
# get text. If we have once locator then index is 0
def get_text(self, locator: str, index: int) -> str:
return self.page.locator(locator).nth(index).text_content()
# Traverse the table
def read_table_data(self, locator) -> []:
table_data = []
table = self.page.locator(locator)
# read table header
header_cells = table.locator(BasePageLocators.TABLE_HEADERS_CELLS)
header_data = []
for h in range(header_cells.count()):
header_cell_text = header_cells.nth(h).inner_text()
header_data.append(header_cell_text)
table_data.append(header_data)
# read table cells by rows
rows = table.locator(BasePageLocators.TABLE_BODY)
for i in range(rows.count()):
row = rows.nth(i)
cells = row.locator("td")
row_data = []
for j in range(cells.count()):
cell_text = cells.nth(j).inner_text()
row_data.append(cell_text)
table_data.append(row_data)
return table_data
# send get request to api
def send_get_api_request(self, uri):
api_request_context = self.get_api_request_context()
token = host.get_access_token()
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
response = api_request_context.get(f"{host.get_base_url()}{uri}", headers = headers)
return response
# send get request to api
def send_post_api_request(self, uri, payload):
api_request_context = self.get_api_request_context()
token = host.get_access_token()
headers = {"Accept": "application/json", "Authorization": f"Bearer {token}"}
response = api_request_context.post(f"{host.get_base_url()}{uri}", headers = headers, data=payload)
return response
# get response body
def get_response_body(self, response):
try:
response_body = response.json()
except json.JSONDecodeError:
print("Failed to decode JSON response")
return None
return response_body
def should_be_horizontal_scroll(self) -> None:
"""
Проверяет горизонтальный скролл с плавной прокруткой.
"""
modal_selector = "div.v-dialog.v-dialog--active"
modal = self.page.locator(modal_selector).first
try:
# Ожидаем появления окна
modal.wait_for(state="visible", timeout=5000)
# Получаем размеры через page.evaluate
scroll_data = self.page.evaluate("""() => {
const modal = document.querySelector('div.v-dialog.v-dialog--active');
return {
scrollWidth: modal.scrollWidth,
clientWidth: modal.clientWidth,
scrollLeft: modal.scrollLeft
};
}""")
print(f"Окно: Общая ширина: {scroll_data['scrollWidth']}px, "
f"Видимая область: {scroll_data['clientWidth']}px, "
f"Начальная позиция: {scroll_data['scrollLeft']}px")
if scroll_data['scrollWidth'] > scroll_data['clientWidth']:
print("Тестируем скролл...")
# Плавный скролл вправо
self.page.evaluate("""() => {
const modal = document.querySelector('div.v-dialog.v-dialog--active');
return new Promise(resolve => {
const target = modal.scrollWidth - modal.clientWidth;
const duration = 1000;
const start = modal.scrollLeft;
const startTime = performance.now();
function scrollStep(timestamp) {
const progress = (timestamp - startTime) / duration;
modal.scrollLeft = start + (target - start) * Math.min(progress, 1);
if (progress < 1) {
window.requestAnimationFrame(scrollStep);
} else {
resolve();
}
}
window.requestAnimationFrame(scrollStep);
});
}""")
# Проверяем конечную позицию
final_scroll = self.page.evaluate("""() => {
return document.querySelector('div.v-dialog.v-dialog--active').scrollLeft;
}""")
print("Успешно проскроллили вправо")
# Плавный скролл влево
self.page.evaluate("""() => {
const modal = document.querySelector('div.v-dialog.v-dialog--active');
return new Promise(resolve => {
const duration = 1000;
const start = modal.scrollLeft;
const startTime = performance.now();
function scrollStep(timestamp) {
const progress = (timestamp - startTime) / duration;
modal.scrollLeft = start - start * Math.min(progress, 1);
if (progress < 1) {
window.requestAnimationFrame(scrollStep);
} else {
resolve();
}
}
window.requestAnimationFrame(scrollStep);
});
}""")
# Проверяем возврат в начало
final_scroll = self.page.evaluate("""() => {
return document.querySelector('div.v-dialog.v-dialog--active').scrollLeft;
}""")
assert final_scroll == 0, "Не удалось вернуться в начальную позицию скролла"
print("Успешно проскроллили влево")
else:
print("Окно не требует горизонтального скролла.")
except Exception as e:
print(f"Ошибка при проверке скролла окна: {str(e)}")
raise
def should_be_vertical_scroll(self, wrapper_selector: str = "div.scrolltable__wrapper") -> None:
"""
Проверяет вертикальный скролл в указанном контейнере.
Args:
wrapper_selector: CSS-селектор скроллируемого контейнера.
По умолчанию: "div.scrolltable__wrapper".
"""
# Ожидаем загрузки таблицы
table_wrapper = self.page.locator(wrapper_selector).first
table_wrapper.wait_for(state="visible", timeout=10000)
try:
# Пытаемся найти скроллируемый элемент с небольшим таймаутом
scrollable_element = table_wrapper.locator("tbody.scrolltable__scroll").first
scrollable_element.wait_for(state="attached", timeout=3000)
# Проверяем параметры скролла
scroll_height = scrollable_element.evaluate("el => el.scrollHeight")
client_height = scrollable_element.evaluate("el => el.clientHeight")
scroll_data = scrollable_element.evaluate("el => el.scrollLeft")
print(f"Окно: Общая ширина: {scroll_height}px, "
f"Видимая область: {client_height}px, "
f"Начальная позиция: {scroll_data}px")
if scroll_height > client_height:
print("Тестируем скролл...")
# 1. Плавный скролл вниз
scrollable_element.evaluate("""el => {
const target = el.scrollHeight;
const duration = 1000;
const start = el.scrollTop;
const startTime = performance.now();
function scrollStep(timestamp) {
const progress = (timestamp - startTime) / duration;
el.scrollTop = start + (target - start) * Math.min(progress, 1);
if (progress < 1) {
window.requestAnimationFrame(scrollStep);
}
}
window.requestAnimationFrame(scrollStep);
}""")
time.sleep(1.5) # Ожидаем завершения скролла
# Проверяем последнюю строку
last_row = table_wrapper.locator("tbody tr").last
expect(last_row).to_be_visible()
print("Успешно проскроллили вниз")
# 2. Плавный скролл вверх
scrollable_element.evaluate("""el => {
const duration = 1000;
const start = el.scrollTop;
const startTime = performance.now();
function scrollStep(timestamp) {
const progress = (timestamp - startTime) / duration;
el.scrollTop = start - start * Math.min(progress, 1);
if (progress < 1) {
window.requestAnimationFrame(scrollStep);
}
}
window.requestAnimationFrame(scrollStep);
}""")
time.sleep(1.5) # Ожидаем завершения скролла
# Проверяем первую строку
first_row = table_wrapper.locator("tbody tr").first
expect(first_row).to_be_visible()
print("Успешно проскроллили вверх")
else:
print("Весь контент виден без скролла")
except Exception as e:
print(f"Элемент для скролла не найден или не скроллится: {e}")
# Проверяем, есть ли хотя бы строки в таблице
rows = table_wrapper.locator("tbody tr")
if rows.count() > 0:
print(f"Найдено {rows.count()} строк в таблице без скролла")
else:
print("Таблица пуста или не найдена")

View File

@ -0,0 +1,49 @@
from pages.base_page import BasePage
from Locators.configuration_page import ConfigurationPageLocators
from data.assertions import Assertions
from playwright.sync_api import Page
import json
class ConfigurationPage(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)
def should_be_configuration_navigation_panel(self):
self.assertion.check_presence(ConfigurationPageLocators.CONFIG_NAVIGATION_PANEL, \
"Configuration navigation panel is not present on the Configuration page")
def should_be_maintenance_navigation_panel(self):
self.assertion.check_presence(ConfigurationPageLocators.MAINTENANCE_NAVIGATION_PANEL, \
"Maintenance navigation panel is not present on the Configuration page")
def click_configuration_navigation_panel_item(self, item_name):
button_locator = None
if item_name == "users":
button_locator = ConfigurationPageLocators.CONFIG_NAVIGATION_PANEL_USER_BUTTON
elif item_name == "notification":
button_locator = ConfigurationPageLocators.CONFIG_NAVIGATION_PANEL_NOTIFICATION_BUTTON
elif item_name == "maintenance":
button_locator = ConfigurationPageLocators.CONFIG_NAVIGATION_PANEL_MAINTENANCE_BUTTON
elif item_name == "ztp":
button_locator = ConfigurationPageLocators.CONFIG_NAVIGATION_PANEL_ZTP_BUTTON
else:
assert False, "Unsupported configuration navigation panel item"
self.click(button_locator)
def click_maintenance_navigation_panel_item(self, item_name):
button_locator = None
if item_name == "session":
button_locator = ConfigurationPageLocators.MAINTENANCE_NAVIGATION_PANEL_SESSION_BUTTON
elif item_name == "service_status":
button_locator = ConfigurationPageLocators.MAINTENANCE_NAVIGATION_PANEL_SERVICE_STATUS_BUTTON
elif item_name == "licensing":
button_locator = ConfigurationPageLocators.MAINTENANCE_NAVIGATION_PANEL_LICENSING_BUTTON
else:
assert False, "Unsupported configuration navigation panel item"
self.click(button_locator)

9
pages/email_tab.py Normal file
View File

@ -0,0 +1,9 @@
from pages.base_page import BasePage
# from Locators.email_tab import EmailTabLocators
from data.assertions import Assertions
from playwright.sync_api import Page
class EmailTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)

102
pages/license_tab.py Normal file
View File

@ -0,0 +1,102 @@
from pages.base_page import BasePage
from Locators.configuration_page import ConfigurationPageLocators
from Locators.license_tab import LicenseTabLocators
from data.assertions import Assertions
from playwright.sync_api import Page
import json
class LicenseTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)
def fill_license_input_form(self, value):
button_text = "Обновить лицензию"
self.clear_input(LicenseTabLocators.LICENSE_INPUT_FORM_TEXTAREA)
self.input(LicenseTabLocators.LICENSE_INPUT_FORM_TEXTAREA, value)
self.page.get_by_role("button", name=button_text).click()
def should_be_error_alert_window_with_text(self, text):
self.assertion.check_alert_window_with_text("error", text)
def should_be_license_work_area(self):
self.assertion.have_title(ConfigurationPageLocators.WORK_AREA_TITLE, "Лицензирование", \
"Expected work area page title is not equal real title")
self.should_be_json_content()
self.should_be_license_input_form()
def should_be_license_input_form(self):
# get form title and compare with cmdb value
form_title = "Идентификатор:"
title = self.get_text(LicenseTabLocators.LICENSE_TITLE, 0)
self.assertion.check_equals(title.strip(), form_title, \
f"Expected input form title {title} is not equal {form_title}")
device_id = self.get_text(LicenseTabLocators.LICENSE_TITLE_DEVICE_ID, 0).strip()
response = self.send_get_api_request("e-cmdb/api/lic/deviceid")
response_body = self.get_response_body(response)
self.assertion.check_equals(device_id, response_body["deviceId"], \
f"Expected ID value {device_id} is not equal {response_body['deviceId']}")
# check input form presence
self.assertion.check_presence(LicenseTabLocators.LICENSE_INPUT_FORM_TEXTAREA, \
"License input form is not present on the License work area")
# check input form button
button_text = "Обновить лицензию"
self.assertion.check_button_presence_with_text(button_text, f"License input form button with text {button_text} is not present")
def should_be_json_content(self):
def format_json_string(json_string):
substrings = json_string.splitlines()
formatted_string_list = []
last_substring = substrings.pop()
for substring in substrings:
if substring.find(':') == -1:
if substring == '{':
formatted_string_list.append(substring)
elif substring == '}':
s1 = formatted_string_list.pop()
formatted_string_list.append(s1.rstrip(','))
formatted_string_list.append(substring+ ',')
else:
formatted_string_list.append(substring + ',')
continue
key, value = substring.split(':')
s = ':'.join(['"'+key+'" '," " + value])
if value == '{':
formatted_string_list.append(s)
else:
formatted_string_list.append(s + ',')
s2 = formatted_string_list.pop()
formatted_string_list.append(s2.rstrip(','))
formatted_string_list.append(last_substring)
return " " .join(formatted_string_list)
## read json_content from work area
json_string = self.page.locator(LicenseTabLocators.JSON_ELEMENT).inner_text()
formatted_json_string = format_json_string(json_string)
expected_json_data = json.loads(formatted_json_string)
# send request to backend to get license info
response = self.send_get_api_request("e-cmdb/api/lic")
response_body = self.get_response_body(response)
## temporarily
del response_body["netManagment"]
response_body["ui"].pop("lcc")
self.assertion.check_json_equals(expected_json_data, response_body, \
"Expected json content is not equal actual:")

59
pages/login_page.py Normal file
View File

@ -0,0 +1,59 @@
from pages.base_page import BasePage
from data.constants import Constants
from Locators.login import LoginPageLocators
from data.assertions import Assertions
from data.environment import host
from playwright.sync_api import Page
class LoginPage(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.token = ""
self.assertion = Assertions(page)
def do_login(self, username: str = None, password: str = None):
"""Выполняет вход в систему.
Если username/password не указаны, использует значения из Constants"""
def handle_response(response):
if "login" in response.url:
response_body = self.get_response_body(response)
if response_body:
token = response_body.get("access_token")
host.set_access_token(token)
self.page.on("response", handle_response)
self.open("")
# Используем переданные значения или значения по умолчанию из Constants
actual_username = username if username is not None else Constants.login
actual_password = password if password is not None else Constants.password
self.clear_input(LoginPageLocators.USERNAME_INPUT)
self.input(LoginPageLocators.USERNAME_INPUT, actual_username)
self.clear_input(LoginPageLocators.PASSWORD_INPUT)
self.input(LoginPageLocators.PASSWORD_INPUT, actual_password)
self.click(LoginPageLocators.LOGIN_BTN)
self.assertion.check_URL("dashboard", "An unexpected page has been opened")
def do_unsuccessful_login(self, username: str = "someuser", password: str = "password"):
"""Выполняет попытку входа с неверными учетными данными.
Можно передать свои неверные данные или использовать значения по умолчанию"""
incorrect_credentials_text = "Неверная пара логин/пароль"
self.open("")
self.clear_input(LoginPageLocators.USERNAME_INPUT)
self.input(LoginPageLocators.USERNAME_INPUT, username)
self.clear_input(LoginPageLocators.PASSWORD_INPUT)
self.input(LoginPageLocators.PASSWORD_INPUT, password)
self.click(LoginPageLocators.LOGIN_BTN)
self.assertion.check_alert_window_with_text("error", incorrect_credentials_text)

42
pages/main_page.py Normal file
View File

@ -0,0 +1,42 @@
from pages.base_page import BasePage
from Locators.main_page import MainPageLocators
from data.assertions import Assertions
from playwright.sync_api import Page
class MainPage(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)
def should_be_navigation_panel(self):
self.assertion.check_presence(MainPageLocators.NAVIGATION_PANEL, "Navigation panel is not present")
def click_main_navigation_panel_item(self, item_name):
button_locator = None
header_locator = None
if item_name == "dashboard":
button_locator = MainPageLocators.NAVIGATION_PANEL_DASHBOARD_BUTTON
header_locator = MainPageLocators.NAVIGATION_PANEL_DASHBOARD_BUTTON_HEADER
elif item_name == "topology":
button_locator = MainPageLocators.NAVIGATION_PANEL_TOPOLOGY_BUTTON
header_locator = MainPageLocators.NAVIGATION_PANEL_TOPOLOGY_BUTTON_HEADER
elif item_name == "configuration":
button_locator = MainPageLocators.NAVIGATION_PANEL_CONFIGURATION_BUTTON
header_locator = MainPageLocators.NAVIGATION_PANEL_CONFIGURATION_BUTTON_HEADER
else:
assert False, "Unsupported main navigation panel item"
self.click(button_locator)
self.assertion.check_URL(item_name, "An unexpected page has been opened")
self.assertion.check_element_active(header_locator, f"{item_name} button is not active")
def do_logout(self):
self.click(MainPageLocators.CURRENT_USER_BUTTON)
self.assertion.check_presence(MainPageLocators.CURRENT_USER_WINDOW, "Window with current user data has not been opened")
logout_button_text = "Выйти"
self.page.get_by_role("button", name=logout_button_text).click()
self.assertion.check_URL("login", "An unexpected page has been opened")

View File

@ -0,0 +1,27 @@
from pages.base_page import BasePage
from Locators.configuration_page import ConfigurationPageLocators
from data.assertions import Assertions
from playwright.sync_api import Page
class ServiceStatusTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)
def should_be_service_status_work_area(self):
self.assertion.have_title(ConfigurationPageLocators.WORK_AREA_TITLE, "Статус обслуживания", \
"Expected work area page title is not equal real title")
self.should_be_service_status_table()
def should_be_service_status_table(self):
headers = ['Контейнер', 'Время создания', 'Статус', 'Время работы', 'Image ID', 'Image ТЭГ']
service_status_table = self.read_table_data(ConfigurationPageLocators.WORK_AREA_TABLE)
service_status_table_headers = service_status_table[0]
self.assertion.check_equals(service_status_table_headers, headers, \
f"Expected table headers {service_status_table_headers} are not equal {headers}")
self.assertion.check_not_equals(len(service_status_table) - 1, 0, \
"Service status table is empty")

221
pages/session_tab.py Normal file
View File

@ -0,0 +1,221 @@
from pages.base_page import BasePage
from Locators.session_locators import SessionLocators
from data.assertions import Assertions
from playwright.sync_api import Page, expect
import time
class SessionTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertions = Assertions(page)
self.expected_headers = ['ID сессии', 'ID пользователя', 'Время жизни', 'Роль', 'Адрес']
def should_be_session_table(self) -> None:
""" Проверка таблицы сессий """
self.assertions.have_title(
SessionLocators.TEXT_TITLE,
'Сессия',
"Заголовок страницы не соответствует ожидаемому"
)
# Проверка наименования заголовков таблицы сессий
session_table_data = self.read_table_data(SessionLocators.TABLE_BODY)
actual_headers = session_table_data[0]
self.assertions.check_equals(actual_headers, self.expected_headers, "Заголовки таблицы не соответствуют ожидаемым")
# Проверка, что таблица не пустая
self.assertions.check_not_equals(len(session_table_data) - 1, 0, "Таблица сессий пуста")
# Проверка наличия всех необходимых колонок
required_columns = [
SessionLocators.TABLE_HEADER_CELL_SESSION_ID,
SessionLocators.TABLE_HEADER_CELL_USER_ID,
SessionLocators.TABLE_HEADER_CELL_LIFETIME,
SessionLocators.TABLE_HEADER_CELL_ROLE,
SessionLocators.TABLE_HEADER_CELL_ADDRESS
]
for locator in required_columns:
self.is_element_present(locator, 1000)
def should_be_session_table_data_vs_api(self) -> None:
""" Проверка соответствие данных в таблице сессий с данными из API """
ROLE_MAPPING = {
'user': 'Пользователь',
'administrator': 'Администратор'
}
# Получение данных из UI
ui_session_id = self.get_text(SessionLocators.TABLE_HEADER_CELL_SESSION_ID, 0).strip()
ui_session_userId = self.get_text(SessionLocators.TABLE_HEADER_CELL_USER_ID, 0).strip()
ui_session_roles = self.get_text(SessionLocators.TABLE_HEADER_CELL_ROLE, 0).strip()
ui_session_ip = self.get_text(SessionLocators.TABLE_HEADER_CELL_ADDRESS, 0).strip()
# Получение данных из API
response = self.send_get_api_request("e-nms/auth/sessions")
response_body = self.get_response_body(response)
api_session_id = response_body[0].get('id')
api_session_userId = response_body[0].get('userId')
api_session_roles = response_body[0].get('roles', [])
api_session_ip = response_body[0].get('ip')
# Преобразование и сортировка ролей из API
translated_roles = [ROLE_MAPPING.get(role.lower(), role) for role in api_session_roles]
api_session_roles_str = ', '.join(sorted(translated_roles))
# Сортировка ролей из UI для сравнения
ui_roles_sorted = ', '.join(sorted(role.strip() for role in ui_session_roles.split(',')))
# Проверки соответствия данных
self.assertions.check_equals(ui_session_id, api_session_id, "ID сессии не совпадает")
self.assertions.check_equals(ui_session_userId, api_session_userId, "ID пользователя не совпадает")
self.assertions.check_equals(ui_roles_sorted, api_session_roles_str, "Роли не совпадают")
self.assertions.check_equals(ui_session_ip, api_session_ip, "IP-адрес не совпадает")
# Логирование для отладки
#print(f"Session ID (UI/API): {ui_session_id}/{api_session_id}")
#print(f"User ID (UI/API): {ui_session_userId}/{api_session_userId}")
#print(f"Roles (UI/API): {ui_session_roles}/{api_session_roles}")
#print(f"IP Address (UI/API): {ui_session_ip}/{api_session_ip}")
def should_be_new_session_added(self) -> None:
"""
Проверка добавленных записей в таблицу сессий
"""
def should_be_vertical_scroll_session(self) -> None:
""" Проверка вертикального скролла таблицы сессий """
self.should_be_vertical_scroll(SessionLocators.TABLE_SCROLL_CONTAINER)
def open_last_session_modal(self) -> None:
"""Открывает модальное окно удаления через последнюю строку таблицы сессий"""
print("\n=== Открытие модального окна через последнюю строку ===")
# Ожидаем загрузки таблицы
self.page.wait_for_selector(SessionLocators.TABLE_BODY, state="visible", timeout=5000)
print("✓ Таблица сессий загружена")
# Получаем все строки таблицы
rows = self.page.locator(SessionLocators.TABLE_ROWS)
row_count = rows.count()
if row_count == 0:
raise AssertionError("Таблица сессий пуста, нечего удалять!")
print(f"Всего строк в таблице: {row_count}")
# Берём последнюю строку
last_row = rows.last
print("✓ Последняя строка получена")
# Прокручиваем к последней строке (если нужно)
last_row.scroll_into_view_if_needed()
# Локатор кнопки удаления в последней строке
delete_button = last_row.locator(SessionLocators.BUTTON_DELETE_SESSION)
# Проверяем и кликаем кнопку удаления
expect(delete_button).to_be_visible(timeout=5000)
print("Нажимаем кнопку удаления в последней строке...")
delete_button.click()
# Проверяем появление модального окна
modal = self.page.locator(SessionLocators.MODAL_WINDOW)
expect(modal).to_be_visible(timeout=5000)
print("✓ Модальное окно удаления успешно открыто")
# Проверка заголовка окна
if hasattr(SessionLocators, 'MODAL_TITLE'):
modal_title = self.page.locator(SessionLocators.MODAL_TITLE)
expect(modal_title).to_contain_text("Удаление")
print("✓ Заголовок модального окна корректен")
print("=== Модальное окно через последнюю строку открыто успешно ===\n")
def should_be_horizontal_scroll_session_modal(self) -> None:
""" Проверка горизонтального скролла модального окна """
self.should_be_horizontal_scroll()
def should_be_close_modal_window_by_button(self, button_type: str) -> None:
"""
Проверка кнопок модального окна подтверждения удаления сессии с детальным логированием
:param button_type: Тип кнопки ('close', 'cancel', 'delete')
"""
print(f"\n=== Начало проверки модального окна для кнопки '{button_type}' ===")
# 1. Проверка видимости модального окна перед действиями
modal = self.page.locator(SessionLocators.MODAL_WINDOW)
expect(modal).to_be_visible()
print("✓ Модальное окно отображается перед взаимодействием")
if button_type == 'close':
# 2. Нажатие кнопки закрытия (X)
print("Нажимаем кнопку закрытия (X)...")
close_button = self.page.locator(SessionLocators.MODAL_CLOSE_BUTTON)
close_button.click()
# 3. Проверка закрытия окна
expect(modal).not_to_be_visible()
print("✓ Проверяем, что окно закрылось после нажатия 'X'")
elif button_type == 'cancel':
# 2. Нажатие кнопки "Отмена"
print("Нажимаем кнопку 'Отмена'...")
cancel_button = self.page.locator(SessionLocators.MODAL_CANCEL_BUTTON)
cancel_button.click()
# 3. Проверка закрытия окна
expect(modal).not_to_be_visible()
print("✓ Проверяем, что окно закрылось после нажатия 'Отмена'")
elif button_type == 'delete':
# 2. Подготовка к удалению
rows_before = self.page.locator(SessionLocators.TABLE_ROWS).count()
print(f"Количество строк до удаления: {rows_before}")
# 3. Нажатие кнопки "Удалить"
print("Нажимаем кнопку 'Удалить'...")
delete_button = self.page.locator(SessionLocators.MODAL_DELETE_BUTTON)
delete_button.click()
"""
# 4. Проверка сообщения об успехе
success_msg = self.page.locator(SessionLocators.SUCCESS_MESSAGE)
expect(success_msg).to_be_visible(timeout=5000)
expect(success_msg).to_contain_text("Сессия успешно удалена")
print("✓ Сообщение об успешном удалении отображается")
"""
# 5. Проверка закрытия окна
expect(modal).not_to_be_visible()
print("✓ Проверяем, что окно закрылось после нажатия 'Удалить'")
"""
# 6. Проверка исчезновения сообщения (если нужно)
expect(success_msg).not_to_be_visible(timeout=10000)
print("✓ Сообщение автоматически скрылось (если предусмотрено UI)")
"""
# 7. Проверка изменения таблицы
rows_after = self.page.locator(SessionLocators.TABLE_ROWS).count()
self.assertions.check_equals(rows_after, rows_before - 1,
"Количество строк не изменилось после удаления")
print(f"✓ Количество строк после удаления: {rows_after} (уменьшилось на 1)")
print(f"=== Проверка для кнопки '{button_type}' успешно завершена ===\n")
def delete_all_created_sessions(self, username) -> None:
""" Проверка удаления созданных ссесий """
def delete_current_session(self) -> None:
""" Проверка удаления текущей сессии """

432
pages/users_tab.py Normal file
View File

@ -0,0 +1,432 @@
from pages.base_page import BasePage
from Locators.base_page import BasePageLocators
from Locators.configuration_page import ConfigurationPageLocators
from Locators.users_tab import UsersTabLocators
from data.assertions import Assertions
from playwright.sync_api import Page
import re
class UsersTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)
self.role_dict = {"administrator": "Администратор",
"manager":"Контактное лицо",
"operator":"Оператор",
"inform_secur_user" : "Специалист информационной безопасности"}
def add_new_user(self, user_data):
fields = user_data.keys()
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM, \
"Input form for add new user is not present")
## input user name
if "name" in fields:
loc = "xpath=div[2]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["name"])
## input user role
if "role" in fields:
loc = "xpath=div[3]/div[2]/div/div/div/div/div[1]"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).click()
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM_ROLES_MENU, \
"Roles drop-down menu is not present")
role = user_data["role"]
self.assertion.check_menu_item_with_text(role, f"No menu item with text: {role}")
self.page.get_by_role("listitem").filter(has_text=role).click()
## input password
if "password" in fields:
loc = "xpath=div[4]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["password"])
## input commentary
if "commentary" in fields:
loc = "xpath=div[5]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["commentary"])
## input e-mail
if "email" in fields:
loc = "xpath=div[6]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["email"])
## input phone number for sms
if "phone_number" in fields:
loc = "xpath=div[7]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["phone_number"])
##to be done: checkbox
## click add user button
add_button_text = "Добавить"
self.assertion.check_button_presence_with_text(add_button_text, f"Add user input form button with text {add_button_text} is not present")
self.page.get_by_role("button", name=add_button_text).click()
##check add user confirmation dialog
confirm_add_button_text = " Добавить "
self.assertion.check_confirmation_dialog_with_title(UsersTabLocators.USER_ACTION_CONFIRMATION_DIALOG, "Добавить нового пользователя")
self.page.get_by_role("button", name=confirm_add_button_text).first.click()
## check message about successfull user addition
self.assertion.check_alert_window_with_text("success", ' Новый пользователь \n успешно добавлен! ')
def close_user_window_by_toolbar_button(self, window_title):
BUTTON_CLOSE_ON_TOOLBAR = f"xpath=(.//*[normalize-space(text()) and normalize-space(.)='{window_title}'])[1]/following::*[name()='svg'][1]"
self.assertion.check_presence(BUTTON_CLOSE_ON_TOOLBAR, \
"Close button is not presenet on toolbar")
self.click(BUTTON_CLOSE_ON_TOOLBAR)
self.assertion.check_absence(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, \
f"{window_title} window should be closed")
def close_user_window(self, window_title):
close_button_text = "Закрыть"
self.assertion.check_button_presence_with_text(close_button_text, f"Edit user input form button with text {close_button_text} is not present")
self.page.get_by_role("button", name=close_button_text).click()
self.assertion.check_absence(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, \
f"{window_title} window should be closed")
def delete_user(self):
remove_button_text = "Удалить"
self.assertion.check_button_presence_with_text(remove_button_text, f"Edit user input form button with text {remove_button_text} is not present")
self.page.get_by_role("button", name=remove_button_text).click()
##check add user confirmation dialog
confirm_remove_button_text = " Удалить "
self.assertion.check_confirmation_dialog_with_title(UsersTabLocators.USER_ACTION_CONFIRMATION_DIALOG, "Удаление")
self.page.get_by_role("button", name=confirm_remove_button_text).first.click()
## check message about successfull user deletion
self.assertion.check_alert_window_with_text("success", '\nПользователь удалён\n')
def edit_user(self, user_data):
fields = user_data.keys()
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM, \
"Input form for edit user is not present")
## input user name
if "name" in fields:
loc = "xpath=div[2]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["name"])
## input user role
if "role" in fields:
loc = "xpath=div[3]/div[2]/div/div/div/div/div[1]"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).click()
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM_ROLES_MENU, \
"Roles drop-down menu is not present")
role = user_data["role"]
self.assertion.check_menu_item_with_text(role, f"No menu item with text: {role}")
self.page.get_by_role("listitem").filter(has_text=role).click()
## input commentary
if "commentary" in fields:
loc = "xpath=div[5]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["commentary"])
## input e-mail
if "email" in fields:
loc = "xpath=div[5]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["email"])
## input phone number for sms
if "phone_number" in fields:
loc = "xpath=div[6]/div[2]/div/div/div/div/input"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).fill(user_data["phone_number"])
##to be done: checkbox
## click add user button
add_button_text = "Сохранить"
self.assertion.check_button_presence_with_text(add_button_text, f"Add user input form button with text {add_button_text} is not present")
self.page.get_by_role("button", name=add_button_text).click()
##check add user confirmation dialog
confirm_add_button_text = " Сохранить "
self.assertion.check_confirmation_dialog_with_title(UsersTabLocators.USER_ACTION_CONFIRMATION_DIALOG, "Сохранение")
self.page.get_by_role("button", name=confirm_add_button_text).first.click()
## check message about successfull user addition. Temporarily without translation
self.assertion.check_alert_window_with_text("success", '\nupdate success\n')
def open_add_user_page(self):
self.assertion.check_presence(UsersTabLocators.TOOLBAR_EDIT_BUTTON, \
"Edit button is not presenet on toolbar")
self.click(UsersTabLocators.TOOLBAR_EDIT_BUTTON)
self.assertion.check_presence(UsersTabLocators.TOOLBAR_ADD_USER_BUTTON, \
"Add User button is not presenet on toolbar")
self.click(UsersTabLocators.TOOLBAR_ADD_USER_BUTTON)
# check that new work area with title has been opened
self.assertion.have_title(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, "Добавить нового пользователя", \
"Expected work area page title is not equal real title")
def open_edit_user_page_by_index(self, row_index):
## temporarily
tmp_dict = {"admin":"Администратор", "manager":"Контактное лицо", "operator":"Оператор"}
users_table = self.read_table_data(ConfigurationPageLocators.WORK_AREA_TABLE)
self.assertion.check_not_equals(len(users_table) - 1, 0, "Users table is empty")
# remove header
del users_table[0]
# check row_index
if row_index > len(users_table):
assert False, "Row_index is out of range"
# get user name and role
user_name = users_table[row_index][0]
for key, val in tmp_dict.items():
if user_name == val:
user_name = key
role = users_table[row_index][2]
# click to found table row
self.page.locator(ConfigurationPageLocators.WORK_AREA_TABLE).locator(BasePageLocators.TABLE_BODY).nth(row_index).click()
# check that edit user work area with user name title has been opened
self.assertion.have_title(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, user_name, \
"Expected edit user work area page title is not equal real title")
return user_name, role
def reset_password(self):
new_password = ""
reset_password_button_text = "Сбросить пароль"
self.assertion.check_button_presence_with_text(reset_password_button_text, f"Edit user input form button with text {reset_password_button_text} is not present")
self.page.get_by_role("button", name=reset_password_button_text).click()
alert_message = self.get_text(BasePageLocators.ALERT_WINDOW_TEXT_SUCCESS, 0)
if len(alert_message) > 0:
new_password = re.findall(r'[\d]+', alert_message)[0]
return new_password
def open_edit_user_page_by_name(self, name, role):
row_index = self.find_user_in_table(name, role)
if row_index == -1:
assert False, f"User with name {name} and role {role} has not been found"
# click to found table row
self.page.locator(ConfigurationPageLocators.WORK_AREA_TABLE).locator(BasePageLocators.TABLE_BODY).nth(row_index).click()
# check that edit user work area with user name title has been opened
self.assertion.have_title(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, name, \
"Expected edit user work area page title is not equal real title")
def should_be_users_work_area(self):
self.assertion.have_title(ConfigurationPageLocators.WORK_AREA_TITLE, "Пользователи", \
"Expected work area page title is not equal real title")
self.assertion.check_presence(UsersTabLocators.TOOLBAR_EDIT_BUTTON, \
"Edit button is not presenet on toolbar")
self.should_be_users_table()
def should_be_users_page_toolbar_buttons(self):
self.assertion.check_presence(UsersTabLocators.TOOLBAR_EDIT_BUTTON, \
"Edit button is not presenet on toolbar")
self.assertion.check_tooltip_with_text(UsersTabLocators.TOOLBAR_EDIT_BUTTON, "Редактировать")
self.click(UsersTabLocators.TOOLBAR_EDIT_BUTTON)
self.assertion.check_presence(UsersTabLocators.TOOLBAR_ADD_USER_BUTTON, \
"Add User button is not presenet on toolbar")
self.assertion.check_presence(UsersTabLocators.TOOLBAR_CLOSE_BUTTON, \
"Close button is not presenet on toolbar")
self.assertion.check_tooltip_with_text(UsersTabLocators.TOOLBAR_ADD_USER_BUTTON, "Добавить")
self.assertion.check_tooltip_with_text(UsersTabLocators.TOOLBAR_CLOSE_BUTTON, "Закрыть")
self.click(UsersTabLocators.TOOLBAR_CLOSE_BUTTON)
self.assertion.check_presence(UsersTabLocators.TOOLBAR_EDIT_BUTTON, \
"Edit button is not presenet on toolbar")
def should_be_add_user_work_area(self):
self.assertion.have_title(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, "Добавить нового пользователя", \
"Expected add user window title is not equal real title")
self.assertion.check_presence(UsersTabLocators.ADD_USER_WORK_AREA_CLOSE_BUTTON, \
"Close button is not presenet on toolbar")
self.assertion.check_tooltip_with_text(UsersTabLocators.ADD_USER_WORK_AREA_CLOSE_BUTTON, "Закрыть")
# check input form
self.should_be_add_user_input_form()
# check input form buttons
add_button_text = "Добавить"
self.assertion.check_button_presence_with_text(add_button_text, f"Add user input form button with text {add_button_text} is not present")
close_button_text = "Закрыть"
self.assertion.check_button_presence_with_text(close_button_text, f"Add user input form button with text {close_button_text} is not present")
def should_be_edit_user_work_area(self, user_name, role):
EDIT_USER_WORK_AREA_CLOSE_BUTTON = f"xpath=(.//*[normalize-space(text()) and normalize-space(.)='{user_name}'])[1]/following::*[name()='svg'][1]"
self.assertion.have_title(UsersTabLocators.USER_DATA_WORK_AREA_TITLE, f"{user_name}", \
"Expected edit user window title is not equal real title")
self.assertion.check_presence(EDIT_USER_WORK_AREA_CLOSE_BUTTON, \
"Close button is not presenet on toolbar")
self.assertion.check_tooltip_with_text(EDIT_USER_WORK_AREA_CLOSE_BUTTON, "Закрыть")
# check edit user input form
self.should_be_edit_user_input_form(user_name, role)
# check input form buttons
save_button_text = "Сохранить"
self.assertion.check_button_presence_with_text(save_button_text, f"Edit user input form button with text {save_button_text} is not present")
remove_button_text = "Удалить"
self.assertion.check_button_presence_with_text(remove_button_text, f"Edit user input form button with text {remove_button_text} is not present")
reset_password_button_text = "Сбросить пароль"
self.assertion.check_button_presence_with_text(reset_password_button_text, f"Edit user input form button with text {reset_password_button_text} is not present")
close_button_text = "Закрыть"
self.assertion.check_button_presence_with_text(close_button_text, f"Edit user input form button with text {close_button_text} is not present")
def should_be_users_table(self):
headers = ['Имя пользователя', 'hash_password', 'Роль', 'E-mail', 'Номер для СМС']
users_table = self.read_table_data(ConfigurationPageLocators.WORK_AREA_TABLE)
users_table_headers = users_table[0]
self.assertion.check_equals(users_table_headers, headers, \
f"Expected table headers {users_table_headers} are not equal {headers}")
self.assertion.check_not_equals(len(users_table) - 1, 0, \
"Users table is empty")
self.verify_users_table_content(users_table)
def should_be_add_user_input_form(self):
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM, \
"Input form for add new user is not present")
n = 7
for i in range (1, n + 1):
loc = f"xpath=div[{i}]/div[2]/div/div/div/div/input"
input_area = self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc)
if i == 1:
checked = self.page.get_by_role("checkbox").first.is_checked()
self.assertion.check_equals(checked, False, "Checkbox is checked by default")
loc = f"xpath={UsersTabLocators.USER_DATA_INPUT_FORM}/div[1]/div[2]/div/div/div{UsersTabLocators.USER_DATA_INPUT_FORM_NOTIFICATION_LABEL}"
self.assertion.have_text(loc, "ad", \
"Label for ad is not present")
elif i == 3:
loc = f"xpath=div[{i}]/div[2]/div/div/div/div/div[1]"
self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).click()
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM_ROLES_MENU, \
"Roles drop-down menu is not present")
roles = self.role_dict.values()
for role in roles:
self.assertion.check_menu_item_with_text(role, f"No menu item with text: {role}")
else:
self.assertion.check_empty_input_area(input_area, "No empty input area")
checked = self.page.get_by_role("checkbox").nth(1).is_checked()
self.assertion.check_equals(checked, False, "Checkbox is checked by default")
loc = f"xpath={UsersTabLocators.USER_DATA_INPUT_FORM}/div[8]/div/div/div/div{UsersTabLocators.USER_DATA_INPUT_FORM_NOTIFICATION_LABEL}"
self.assertion.have_text(loc, "Подписка на Push-уведомления", \
"Label for push-notifications subscribtion is not present")
def should_be_edit_user_input_form(self, user_name, role):
self.assertion.check_presence(UsersTabLocators.USER_DATA_INPUT_FORM, \
"Input form for edit user is not present")
loc = "xpath=div[2]/div[2]/div/div/div/div/input"
text_value = self.page.locator(UsersTabLocators.USER_DATA_INPUT_FORM).locator(loc).input_value()
self.assertion.check_equals(text_value, user_name, "Expected user name is not equal real user name")
self.assertion.check_menu_item_with_text(role, f"No menu item with text: {role}")
def should_be_user_in_table(self, name, role):
found = self.find_user_in_table(name, role)
if found == -1:
assert False, f"User with name {name} and role {role} has not been found"
def should_not_be_user_in_table(self, name, role):
found = self.find_user_in_table(name, role)
if found != -1:
assert False, f"User with name {name} and role {role} has been found"
def find_user_in_table(self, name, role):
users_table = self.read_table_data(ConfigurationPageLocators.WORK_AREA_TABLE)
self.assertion.check_not_equals(len(users_table) - 1, 0, "Users table is empty")
# remove header
del users_table[0]
not_found_index = -1
row_index = 0
for user_info in users_table:
if name in user_info and role in user_info:
return row_index
row_index += 1
return not_found_index
def verify_users_table_content(self, users_table):
expected_users_list = []
## temporarily
tmp_dict = {"admin":"Администратор", "manager":"Контактное лицо", "operator":"Оператор"}
query = {"id": ["/catalogs/user"], "data": {"namePath": True, "children": {"flatten": True}}}
response = self.send_post_api_request("e-cmdb/api/query", query)
response_body = self.get_response_body(response)
for item in response_body[0]["children"]:
user_info = []
## temporarily
user_name = item["name"]
if user_name in tmp_dict.keys():
item["name"] = tmp_dict[user_name]
user_info.append(item["name"])
if item["password"] is not None:
user_info.append(item["password"])
else:
user_info.append("")
if item["role"] is not None:
role = item["role"]
if role in self.role_dict.keys():
item["role"] = self.role_dict[role]
user_info.append(item["role"])
else:
user_info.append("")
if item["email"] is not None:
user_info.append(item["email"])
else:
user_info.append("")
if item["sms_phone"] is not None:
user_info.append(item["sms_phone"])
else:
user_info.append("")
expected_users_list.append(user_info)
# remove header
del users_table[0]
self.assertion.check_lists_equals(users_table, expected_users_list, \
"Actual users list is not equal users list from db")

View File

@ -0,0 +1,9 @@
from pages.base_page import BasePage
# from Locators.ztp_tab import ZTPTabLocators
from data.assertions import Assertions
from playwright.sync_api import Page
class ZTPConfigurationTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)

View File

@ -0,0 +1,9 @@
from pages.base_page import BasePage
# from Locators.ztp_tab import ZTPTabLocators
from data.assertions import Assertions
from playwright.sync_api import Page
class ZTPTemplatesTab(BasePage):
def __init__(self, page: Page) -> None:
super().__init__(page)
self.assertion = Assertions(page)

4
pytest.ini Normal file
View File

@ -0,0 +1,4 @@
[pytest]
markers =
develop: current test development
addopts = -v -s

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
pytest
playwright
requests
qase-pytest==4.2.0
python-dotenv
jsondiff

99
tests/test_license_tab.py Normal file
View File

@ -0,0 +1,99 @@
import pytest
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.configuration_page import ConfigurationPage
from pages.license_tab import LicenseTab
import uuid
## @pytest.mark.smoke
class TestLicenseTab:
def test_license_tab_content(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Technical Maintenance button in configuration navigation panel
cp.click_configuration_navigation_panel_item("maintenance")
# check Maintenance navigation panel presence
cp.should_be_maintenance_navigation_panel()
# click to Service Status button in configuration navigation panel
cp.click_maintenance_navigation_panel_item("licensing")
# license tab has been opened
lt = LicenseTab(browser)
# check service status work area
lt.should_be_license_work_area()
def test_license_tab_input_form_and_check_alert(self, browser):
def gen_test_data():
data = []
for i in range(3):
data.append(uuid.uuid4().hex)
lowercase_str = uuid.uuid4().hex
data.append(lowercase_str.upper())
data.append(lowercase_str+"fffffffff")
data.append("0")
data.append("000000000000000000000000000000000000000000000000")
data.append("-1")
return data
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Technical Maintenance button in configuration navigation panel
cp.click_configuration_navigation_panel_item("maintenance")
# check Maintenance navigation panel presence
cp.should_be_maintenance_navigation_panel()
# click to Service Status button in configuration navigation panel
cp.click_maintenance_navigation_panel_item("licensing")
# license tab has been opened
lt = LicenseTab(browser)
# check that license input form presents
lt.should_be_license_input_form()
# input empty string
lt.fill_license_input_form("")
# check alert window
lt.should_be_error_alert_window_with_text("Неверный лицензионный ключ")
data = gen_test_data()
for data_string in data:
# input incorrect value
lt.fill_license_input_form(data_string)
# check alert window
lt.should_be_error_alert_window_with_text("Ошибка обновления лицензии")

23
tests/test_login.py Normal file
View File

@ -0,0 +1,23 @@
import pytest
from pages.login_page import LoginPage
from pages.main_page import MainPage
## @pytest.mark.smoke
class TestLogin:
def test_successful_login(self, browser):
lp = LoginPage(browser)
lp.do_login()
def test_unsuccessful_login(self, browser):
lp = LoginPage(browser)
lp.do_unsuccessful_login()
def test_successful_login_and_logout(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# do logout
mp.do_logout()

54
tests/test_scroll_tab.py Normal file
View File

@ -0,0 +1,54 @@
import pytest
import time
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.configuration_page import ConfigurationPage
from pages.session_tab import SessionTab
from pages.scroll_tab import ScrollTab
## @pytest.mark.smoke
class TestSessionTab:
def test_session_tab_content(self, browser):
lp1 = LoginPage(browser)
lp1.do_login()
#'''
users = [f"User{i}" for i in range(1, 9)] # Генерация списка пользователей
for username in users:
login_page = LoginPage(browser)
login_page.do_login(username=username, password="admin")
#'''
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
sc = ScrollTab(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
sc.check_vertical_scroll("//*[@id='app']/div[150]/div/div[1]/div/div[1]/div/div[2]/div/div/div/div/div/div/div")
# click to Technical Maintenance button in configuration navigation panel
cp.click_configuration_navigation_panel_item("maintenance")
cp.should_be_maintenance_navigation_panel()
# click to Session button in configuration navigation panel
cp.click_maintenance_navigation_panel_item("session")
sc.check_vertical_scroll("div.layout.white.column.fill-height")
# click to Service Status button in configuration navigation panel
cp.click_maintenance_navigation_panel_item("service_status")
sc.check_vertical_scroll("//*[@id='app']/div[151]/div/div[1]/div/div[1]/div/div[2]/div/div/div/div/div/div/div")

View File

@ -0,0 +1,39 @@
import pytest
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.configuration_page import ConfigurationPage
from pages.service_status_tab import ServiceStatusTab
## @pytest.mark.smoke
class TestServiceStatusTab:
def test_service_status_tab_content(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Technical Maintenance button in configuration navigation panel
cp.click_configuration_navigation_panel_item("maintenance")
# check Maintenance navigation panel presence
cp.should_be_maintenance_navigation_panel()
# click to Service Status button in configuration navigation panel
cp.click_maintenance_navigation_panel_item("service_status")
# service status tab has been opened
sst = ServiceStatusTab(browser)
# check service status work area
sst.should_be_service_status_work_area()

162
tests/test_session_tab.py Normal file
View File

@ -0,0 +1,162 @@
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.users_tab import UsersTab
from pages.configuration_page import ConfigurationPage
from pages.session_tab import SessionTab
import pytest
## @pytest.mark.smoke # Закомментированная метка для smoke-тестов
class TestSessionTab:
def test_add_users(self, browser):
"""Тест создания 9 пользователей User1..User9 с ролью Администратор"""
# Переходим на страницу авторизации
lp = LoginPage(browser)
# Выполняем вход в систему (по умолчанию)
lp.do_login()
# Переходим на главную страницу
mp = MainPage(browser)
# Проверяем наличие панели навигации
mp.should_be_navigation_panel()
# Нажимаем на кнопку "Configuration" в главной панели навигации
mp.click_main_navigation_panel_item("configuration")
# Переходим на страницу конфигурации
cp = ConfigurationPage(browser)
# Нажимаем вкладку "Users" в панели навигации конфигурации
cp.click_configuration_navigation_panel_item("users")
# Переходим на вкладку пользователей
ut = UsersTab(browser)
# Создаем 9 пользователей
for i in range(1, 10):
user_data = {
"name": f"User{i}", # Генерируем имя пользователя
"role": "Администратор", # Устанавливаем роль
"password": "admin" # Пароль для всех пользователей
}
ut.open_add_user_page() # Открываем страницу добавления пользователя
ut.add_new_user(user_data) # Добавляем пользователя с заданными данными
# Двойной клик по вкладке Users ( баг )
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# Проверяем, что пользователь появился в таблице
ut.should_be_user_in_table(user_data["name"], user_data["role"])
def test_login_users(self, browser):
"""Тест входа под созданными пользователями (создание 9 сессий)"""
# Генерируем список имен пользователей
users = [f"User{i}" for i in range(1, 9)]
# Выполняем вход в систему 9 созданными пользователями
for username in users:
login_page = LoginPage(browser)
login_page.do_login(username=username, password="admin")
def test_session_tab_content(self, browser):
"""Тест проверки содержимого вкладки сессий"""
lp = LoginPage(browser)
lp.do_login()
mp = MainPage(browser)
# Проверяем наличие панели навигации
mp.should_be_navigation_panel()
# Нажимаем на кнопку "Configuration" в главной панели навигации
mp.click_main_navigation_panel_item("configuration")
# Переходим на страницу конфигурации
cp = ConfigurationPage(browser)
# Нажимаем на кнопку "Technical Maintenance" в панели навигации конфигурации
cp.click_configuration_navigation_panel_item("maintenance")
# Проверяем наличие панели навигации Maintenance
cp.should_be_maintenance_navigation_panel()
# Нажимаем на вкладку "Session" в панели навигации Maintenanc
cp.click_maintenance_navigation_panel_item("session")
# Работаем с вкладкой сессий
st = SessionTab(browser)
# Проверяем наличие таблицы сессий
st.should_be_session_table()
# Проверяем соответствие данных в таблице данным из API
st.should_be_session_table_data_vs_api()
#st.should_be_new_session_added() требуется разработать
# Проверяем наличие вертикальной прокрутки в таблице сессий
st.should_be_vertical_scroll_session()
# Открываем модальное окно последней сессии
st.open_last_session_modal()
# Проверяем наличие горизонтальной прокрутки в модальном окне
st.should_be_horizontal_scroll_session_modal()
# Закрываем модальное окно кнопкой "close"
st.should_be_close_modal_window_by_button("close")
# Закрываем модальное окно кнопкой "cancel"
st.open_last_session_modal()
st.should_be_close_modal_window_by_button("cancel")
# Закрываем модальное окно кнопкой "delete"
st.open_last_session_modal()
st.should_be_close_modal_window_by_button("delete")
def test_delete_users(self, browser):
"""Тест удаления созданных пользователей (в обратном порядке)"""
lp = LoginPage(browser)
lp.do_login()
# Переходим на главную страницу
mp = MainPage(browser)
# Проверяем наличие панели навигации
mp.should_be_navigation_panel()
# Нажимаем на кнопку "Configuration" в главной панели навигации
mp.click_main_navigation_panel_item("configuration")
# Нажимаем на страницу конфигурации
cp = ConfigurationPage(browser)
# Нажимаем на вкладку "Users" в панели навигации конфигурации
cp.click_configuration_navigation_panel_item("users")
# Работаем с вкладкой пользователей
ut = UsersTab(browser)
# Удаляем пользователей в обратном порядке (от User8 до User1)
for i in range(9, 0, -1):
user_data = {
"name": f"User{i}",
"role": "Администратор",
}
ut.open_edit_user_page_by_name(user_data["name"], user_data["role"])
ut.delete_user()
# Двойной клик по вкладке Users (возможно, для обновления списка)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# Проверяем отсутствие последнего удаленного пользователя в таблице
ut.should_not_be_user_in_table(user_data["name"], user_data["role"])

334
tests/test_users_tab.py Normal file
View File

@ -0,0 +1,334 @@
import pytest
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.configuration_page import ConfigurationPage
from pages.users_tab import UsersTab
class TestUsersTab:
def test_users_tab_content(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# check users work area
ut.should_be_users_work_area()
def test_users_tab_toolbar_buttons(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
ut.should_be_users_page_toolbar_buttons()
def test_add_user_window_content(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open add new user page
ut.open_add_user_page()
# check add user work area
ut.should_be_add_user_work_area()
def test_edit_user_window_content(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open edit user page
user_name, role = ut.open_edit_user_page_by_index(0)
# check edit user work area
ut.should_be_edit_user_work_area(user_name, role)
def test_edit_user_window_close_buttons(self, browser):
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open edit user page
user_name, role = ut.open_edit_user_page_by_index(0)
ut.close_user_window_by_toolbar_button(user_name)
# open edit user page
user_name, role = ut.open_edit_user_page_by_index(0)
ut.close_user_window(user_name)
def test_add_and_delete_user(self, browser):
user_data = {"name": "User", "role": "Администратор", "password": "admin"}
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open add new user page
ut.open_add_user_page()
# create new user
ut.add_new_user(user_data)
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check that new user has been added
ut.should_be_user_in_table(user_data["name"], user_data["role"])
# open edit user page and delete user
ut.open_edit_user_page_by_name(user_data["name"], user_data["role"])
ut.delete_user()
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check user abcense in users list
ut.should_not_be_user_in_table(user_data["name"], user_data["role"])
def test_reset_password(self, browser):
user_data = {"name": "autoadmin", "role": "Администратор", "password": "12345"}
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open add new user page
ut.open_add_user_page()
# create new user
ut.add_new_user(user_data)
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check that new user has been added
ut.should_be_user_in_table(user_data["name"], user_data["role"])
# open edit user page
ut.open_edit_user_page_by_name(user_data["name"], user_data["role"])
new_password = ut.reset_password()
if len(new_password) == 0: assert False, "Unsuccessful password reset"
new_lp = LoginPage(browser)
new_lp.do_login(username=user_data["name"], password=new_password)
# we are on main page
new_mp = MainPage(browser)
# do logout
new_mp.do_logout()
lp_1 = LoginPage(browser)
lp_1.do_login()
# we are on main page
mp_1 = MainPage(browser)
# click to Configuration button in main navigation panel
mp_1.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp_1 = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp_1.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut_1 = UsersTab(browser)
# open edit user page and delete user
ut_1.open_edit_user_page_by_name(user_data["name"], user_data["role"])
ut_1.delete_user()
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp_1.click_configuration_navigation_panel_item("users")
cp_1.click_configuration_navigation_panel_item("users")
# check user abcense in users list
ut_1.should_not_be_user_in_table(user_data["name"], user_data["role"])
def test_edit_user_role(self, browser):
user_data = {"name": "autooperator", "role": "Оператор", "password": "auto123@"}
lp = LoginPage(browser)
lp.do_login()
# we are on main page
mp = MainPage(browser)
# check navigation panel presence
mp.should_be_navigation_panel()
# click to Configuration button in main navigation panel
mp.click_main_navigation_panel_item("configuration")
# we are on configuration page
cp = ConfigurationPage(browser)
# click to Users button in configuration navigation panel
cp.click_configuration_navigation_panel_item("users")
# users tab has been opened
ut = UsersTab(browser)
# open add new user page
ut.open_add_user_page()
# create new user
ut.add_new_user(user_data)
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check that new user has been added
ut.should_be_user_in_table(user_data["name"], user_data["role"])
# open edit user page and delete user
ut.open_edit_user_page_by_name(user_data["name"], user_data["role"])
new_user_data = {}
new_user_data["role"] = "Контактное лицо"
ut.edit_user(new_user_data)
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check that new user has been added
ut.should_be_user_in_table(user_data["name"], new_user_data["role"])
# open edit user page and delete user
ut.open_edit_user_page_by_name(user_data["name"], new_user_data["role"])
ut.delete_user()
# click to Users button in configuration navigation panel to update users list (two times is bug?)
cp.click_configuration_navigation_panel_item("users")
cp.click_configuration_navigation_panel_item("users")
# check user abcense in users list
ut.should_not_be_user_in_table(user_data["name"], new_user_data["role"])