Правка code style, актуализация тестов под текущие изменения GUI
parent
ce6fe0390c
commit
10315ba38f
|
|
@ -7,4 +7,9 @@ tooltiplocator
|
|||
- components\toolbar_component.py - добавлен tooltiplocator в сигнатуру функции check_button_tooltip, изменены функции add_button и get_button_by_name
|
||||
- pages\users_tab.py - переписана функция should_be_toolbar_buttons
|
||||
- pages\session_tab.py - вкладка "Сессии"
|
||||
- tests\e2e\test_sessions_tab.py - тест вкладки "Сессии"
|
||||
- tests\e2e\test_sessions_tab.py - тест вкладки "Сессии"
|
||||
===================23.07.2025==========================
|
||||
- Все файлы прошли проверку pylint, внесены исправления для фикса замечаний линтера
|
||||
- Возвращено заведение пользователя с введением пароля
|
||||
- Актуализированы тесты под текущее состояние интерфейса пользователя версии 1.7
|
||||
|
||||
|
|
@ -1,18 +1,16 @@
|
|||
from playwright.sync_api import Page, expect
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from elements.text_element import Text
|
||||
|
||||
from tools.logger import get_logger
|
||||
from elements.text_element import Text
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("ALERT")
|
||||
|
||||
|
||||
class AlertComponent(BaseComponent):
|
||||
"""Компонент для работы с alert-окнами.
|
||||
|
||||
|
||||
Поддерживает различные типы alert-окон: error, success, info, warning.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page: экземпляр страницы Playwright
|
||||
alert_type: тип alert-окна (error/success/info/warning)
|
||||
|
|
@ -21,69 +19,74 @@ class AlertComponent(BaseComponent):
|
|||
|
||||
def __init__(self, page: Page, alert_type: str):
|
||||
"""Инициализация компонента alert-окна.
|
||||
|
||||
|
||||
Args:
|
||||
page: экземпляр страницы Playwright
|
||||
alert_type: тип alert-окна (error/success/info/warning)
|
||||
|
||||
|
||||
Raises:
|
||||
ValueError: если передан неподдерживаемый тип alert-окна
|
||||
"""
|
||||
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
alert_types = ["error", "success", "info", "warning"]
|
||||
if alert_type not in alert_types:
|
||||
raise ValueError("Unsupported type of alert window")
|
||||
|
||||
|
||||
self.alert_type = alert_type
|
||||
self.text = Text(page, f"//div[@class='v-alert {self.alert_type}']/div", "Alert message")
|
||||
self.text = Text(page, f"//div[@class='v-alert {self.alert_type}']/div", "Alert message")
|
||||
|
||||
# Действия:
|
||||
def get_text(self) -> str:
|
||||
"""Получение текста сообщения из alert-окна.
|
||||
|
||||
|
||||
Returns:
|
||||
str: текст сообщения alert-окна
|
||||
"""
|
||||
|
||||
return self.text.get_text(0)
|
||||
|
||||
|
||||
# Проверки:
|
||||
def check_presence(self, text: str):
|
||||
def check_alert_presence(self, text: str):
|
||||
"""Проверка наличия alert-окна с заданным текстом.
|
||||
|
||||
|
||||
Args:
|
||||
text: текст для проверки (если пустая строка - проверяется только наличие окна)
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: если alert-окно не найдено
|
||||
"""
|
||||
|
||||
msg = f"No {self.alert_type} alert window on page"
|
||||
if text == "":
|
||||
expect(self.page.get_by_role("alert")).to_be_visible(), msg
|
||||
else:
|
||||
expect(self.page.get_by_role("alert").filter(has_text=text)).to_be_visible(), msg
|
||||
|
||||
def check_absence(self, text: str, timeout: int = 30000):
|
||||
|
||||
def check_alert_absence(self, text: str, timeout: int = 30000):
|
||||
"""Проверка отсутствия alert-окна с заданным текстом.
|
||||
|
||||
|
||||
Args:
|
||||
text: текст для проверки
|
||||
timeout: время ожидания исчезновения (в миллисекундах)
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: если alert-окно не исчезает в течение заданного времени
|
||||
"""
|
||||
seconds = int(timeout/1000)
|
||||
msg = f"Alert {self.alert_type} window should disappear after {seconds} seconds"
|
||||
msg = f"Alert {self.alert_type} window should disappear after {seconds} seconds"
|
||||
expect(self.page.get_by_role("alert").filter(has_text=text)).to_be_hidden(timeout=timeout), msg
|
||||
|
||||
def check_text(self, alert_text: str):
|
||||
"""Проверка точного соответствия текста в alert-окне.
|
||||
|
||||
|
||||
Args:
|
||||
alert_text: ожидаемый текст сообщения
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: если текст не соответствует ожидаемому
|
||||
"""
|
||||
self.text.check_have_text(alert_text, f"Unexpected message in alert {self.alert_type} window")
|
||||
|
||||
self.text.check_have_text(alert_text,
|
||||
f"Unexpected message in alert {self.alert_type} window")
|
||||
|
|
|
|||
|
|
@ -1,48 +1,171 @@
|
|||
from playwright.sync_api import Page, Locator, expect
|
||||
|
||||
from tools.logger import get_logger
|
||||
|
||||
logger = get_logger("BASE_COMPONENT")
|
||||
|
||||
|
||||
class BaseComponent:
|
||||
"""Базовый компонент для работы с элементами страницы."""
|
||||
|
||||
"""Базовый компонент для работы с элементами страницы.
|
||||
|
||||
Предоставляет общие методы для взаимодействия с элементами:
|
||||
- получение локаторов
|
||||
- проверка видимости элементов
|
||||
- работа с прокруткой
|
||||
|
||||
Атрибуты:
|
||||
page: экземпляр страницы Playwright
|
||||
"""
|
||||
|
||||
def __init__(self, page: Page):
|
||||
"""Инициализация базового компонента.
|
||||
|
||||
Args:
|
||||
page: экземпляр страницы Playwright
|
||||
"""
|
||||
self.page = page
|
||||
|
||||
|
||||
# Действия:
|
||||
def get_locator(self, locator: str | Locator) -> Locator:
|
||||
"""Получение объекта Locator из строки или существующего Locator.
|
||||
|
||||
Args:
|
||||
locator: строка с CSS/XPath селектором или объект Locator
|
||||
|
||||
Returns:
|
||||
Locator: объект для работы с элементом
|
||||
|
||||
Raises:
|
||||
TypeError: если передан некорректный тип локатора
|
||||
"""
|
||||
if isinstance(locator, Locator):
|
||||
return locator
|
||||
elif isinstance(locator, str):
|
||||
return self.page.locator(locator)
|
||||
else:
|
||||
raise TypeError("locator value should be string type or Locator type")
|
||||
|
||||
def check_presence(self, locator: str | Locator, msg: str):
|
||||
|
||||
# Закомментированный код сохранен без изменений
|
||||
# def wait_for_all_elements(self, locator: Locator, timeout=5000):
|
||||
# loc = self.get_locator(locator)
|
||||
# elements = self.page.locator(loc).all()
|
||||
#
|
||||
# for element in elements:
|
||||
# self.page.locator(loc).wait_for(timeout=timeout)
|
||||
#
|
||||
# return elements
|
||||
|
||||
# Проверки:
|
||||
def check_presence(self, locator, msg):
|
||||
"""Проверка видимости элемента на странице.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента (строка или объект Locator)
|
||||
msg: сообщение об ошибке при неудачной проверке
|
||||
|
||||
Raises:
|
||||
AssertionError: если элемент не виден на странице
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
expect(loc).to_be_visible(visible=True, timeout=12000), msg
|
||||
|
||||
def is_scrollable_vertically(self, locator: str | Locator) -> bool:
|
||||
|
||||
def is_scrollable_vertically(self, locator) -> bool:
|
||||
"""Проверка возможности вертикальной прокрутки элемента.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Returns:
|
||||
bool: True если элемент можно прокрутить вертикально
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
return loc.evaluate("el => el.scrollHeight > el.clientHeight")
|
||||
|
||||
def is_scrollable_horizontally(self, locator: str | Locator) -> bool:
|
||||
|
||||
def is_scrollable_horizontally(self, locator) -> bool:
|
||||
"""Проверка возможности горизонтальной прокрутки элемента.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Returns:
|
||||
bool: True если элемент можно прокрутить горизонтально
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
return loc.evaluate("el => el.scrollWidth > el.clientWidth")
|
||||
|
||||
def scroll_up(self, locator: str | Locator):
|
||||
|
||||
# Методы прокрутки:
|
||||
def scroll_up(self, locator):
|
||||
"""Прокрутка элемента вверх.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Raises:
|
||||
AssertionError: если прокрутка не выполнена до конца
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
loc.evaluate("el => el.scrollTo(0, 0)")
|
||||
|
||||
def scroll_down(self, locator: str | Locator):
|
||||
loc.wait_for(timeout=2000)
|
||||
|
||||
# Проверка позиции прокрутки
|
||||
scroll_position = loc.evaluate("el => el.scrollTop")
|
||||
assert scroll_position == 0, "Invalid postion after scroll up"
|
||||
|
||||
def scroll_down(self, locator):
|
||||
"""Прокрутка элемента вниз.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Raises:
|
||||
AssertionError: если прокрутка не выполнена до конца
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
loc.evaluate("el => el.scrollTo(0, el.scrollHeight)")
|
||||
|
||||
def scroll_left(self, locator: str | Locator):
|
||||
loc.wait_for(timeout=2000)
|
||||
|
||||
# Проверка позиции прокрутки
|
||||
scroll_position = loc.evaluate("el => el.scrollTop")
|
||||
assert scroll_position > 0, "Invalid postion after scroll down"
|
||||
|
||||
def scroll_left(self, locator):
|
||||
"""Прокрутка элемента влево.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Raises:
|
||||
AssertionError: если прокрутка не выполнена до конца
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
|
||||
width = loc.evaluate("el => el.scrollWidth")
|
||||
|
||||
def scroll_right(self, locator: str | Locator):
|
||||
loc.scroll_into_view_if_needed()
|
||||
self.page.mouse.wheel(-width, 0)
|
||||
|
||||
loc.wait_for(timeout=2000)
|
||||
|
||||
# Проверка позиции прокрутки
|
||||
scroll_position = loc.evaluate("el => el.scrollLeft")
|
||||
assert scroll_position == 0, "Invalid postion after scroll left"
|
||||
|
||||
def scroll_right(self, locator):
|
||||
"""Прокрутка элемента вправо.
|
||||
|
||||
Args:
|
||||
locator: локатор элемента
|
||||
|
||||
Raises:
|
||||
AssertionError: если прокрутка не выполнена до конца
|
||||
"""
|
||||
loc = self.get_locator(locator)
|
||||
width = loc.evaluate("el => el.scrollWidth")
|
||||
|
||||
width = loc.evaluate("el => el.scrollWidth")
|
||||
loc.scroll_into_view_if_needed()
|
||||
self.page.mouse.wheel(width, 0)
|
||||
|
||||
loc.wait_for(timeout=2000)
|
||||
|
||||
# Проверка позиции прокрутки
|
||||
scroll_position = loc.evaluate("el => el.scrollLeft")
|
||||
max_scroll_x = loc.evaluate("el => el.scrollWidth - el.clientWidth")
|
||||
assert scroll_position >= max_scroll_x, "Invalid postion after scroll right"
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
from playwright.sync_api import Page
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from elements.button_element import Button
|
||||
|
||||
from tools.logger import get_logger
|
||||
from elements.button_element import Button
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("USER_CARD")
|
||||
|
||||
|
||||
class CardComponent(BaseComponent):
|
||||
"""Компонент карточки пользователя.
|
||||
|
||||
|
||||
Предоставляет методы для взаимодействия с элементами карточки пользователя.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page: экземпляр страницы Playwright
|
||||
logout_button: кнопка выхода из системы
|
||||
|
|
@ -20,25 +18,25 @@ class CardComponent(BaseComponent):
|
|||
|
||||
def __init__(self, page: Page):
|
||||
"""Инициализация компонента карточки пользователя.
|
||||
|
||||
|
||||
Args:
|
||||
page: экземпляр страницы Playwright
|
||||
"""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.logout_button = Button(
|
||||
page,
|
||||
page.get_by_role("button", name="Выйти"),
|
||||
page,
|
||||
page.get_by_role("button", name="Выйти"),
|
||||
"logout button"
|
||||
)
|
||||
|
||||
|
||||
# Действия:
|
||||
def click_logout_button(self):
|
||||
"""Нажатие кнопки выхода из системы.
|
||||
|
||||
|
||||
Выполняет клик по кнопке 'Выйти' в карточке пользователя.
|
||||
"""
|
||||
self.logout_button.click()
|
||||
|
||||
|
||||
# Проверки:
|
||||
# (Методы проверок могут быть добавлены здесь в будущем)
|
||||
# (Методы проверок могут быть добавлены здесь в будущем)
|
||||
|
|
|
|||
|
|
@ -1,54 +1,52 @@
|
|||
from playwright.sync_api import Page
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from elements.button_element import Button
|
||||
from elements.text_element import Text
|
||||
from locators.confirm_locators import ConfirmLocators
|
||||
|
||||
from tools.logger import get_logger
|
||||
from locators.confirm_locators import ConfirmLocators
|
||||
from elements.text_element import Text
|
||||
from elements.button_element import Button
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("CONFIRM_WINDOW")
|
||||
|
||||
|
||||
class ConfirmComponent(BaseComponent):
|
||||
"""Компонент окна подтверждения действий."""
|
||||
|
||||
|
||||
def __init__(self, page: Page, cancel_button_text: str, allow_button_text: str):
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.title = Text(page, ConfirmLocators.TITLE, "confirm title")
|
||||
self.text = Text(page, ConfirmLocators.TEXT, "confirm text")
|
||||
|
||||
|
||||
self.close_button = Button(page, ConfirmLocators.BUTTON_CLOSE, "confirm close button")
|
||||
self.cancel_button = Button(
|
||||
page,
|
||||
page.get_by_role("button", name=cancel_button_text).first,
|
||||
page,
|
||||
page.get_by_role("button", name=cancel_button_text).first,
|
||||
"confirm cancel button"
|
||||
)
|
||||
self.allow_button = Button(
|
||||
page,
|
||||
page.get_by_role("button", name=allow_button_text).first,
|
||||
page,
|
||||
page.get_by_role("button", name=allow_button_text).first,
|
||||
"confirm allow button"
|
||||
)
|
||||
|
||||
|
||||
# Действия:
|
||||
def click_allow_button(self) -> None:
|
||||
"""Нажатие кнопки подтверждения действия."""
|
||||
self.allow_button.click()
|
||||
|
||||
|
||||
def click_cancel_button(self) -> None:
|
||||
"""Нажатие кнопки отмены действия."""
|
||||
self.cancel_button.click()
|
||||
|
||||
|
||||
def click_close_button(self) -> None:
|
||||
"""Нажатие кнопки закрытия окна подтверждения."""
|
||||
self.close_button.click()
|
||||
|
||||
|
||||
# Проверки:
|
||||
def check_title(self, title: str, msg: str) -> None:
|
||||
"""Проверка текста заголовка окна подтверждения."""
|
||||
self.title.check_have_text(title, msg)
|
||||
|
||||
|
||||
def check_text(self, text: str, msg: str) -> None:
|
||||
"""Проверка текста сообщения в окне подтверждения."""
|
||||
self.text.check_have_text(text, msg)
|
||||
self.text.check_have_text(text, msg)
|
||||
|
|
|
|||
|
|
@ -1,58 +1,57 @@
|
|||
from playwright.sync_api import Page
|
||||
import json
|
||||
import jsondiff
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from playwright.sync_api import Page
|
||||
from tools.logger import get_logger
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("JSON_CONTAINER")
|
||||
|
||||
|
||||
class JsonContainerComponent(BaseComponent):
|
||||
"""Компонент для работы с JSON-данными на странице.
|
||||
|
||||
|
||||
Предоставляет методы для чтения и проверки JSON-данных,
|
||||
отображаемых в специальных контейнерах на странице.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page: экземпляр страницы Playwright
|
||||
"""
|
||||
|
||||
def __init__(self, page: Page):
|
||||
"""Инициализация JSON-контейнера.
|
||||
|
||||
|
||||
Args:
|
||||
page: экземпляр страницы Playwright
|
||||
"""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
# Действия:
|
||||
def read_data(self, locator):
|
||||
"""Чтение и форматирование JSON-данных из указанного локатора.
|
||||
|
||||
|
||||
Args:
|
||||
locator: локатор элемента, содержащего JSON-данные
|
||||
|
||||
|
||||
Returns:
|
||||
dict: распарсенный JSON-объект
|
||||
|
||||
|
||||
Raises:
|
||||
json.JSONDecodeError: если данные не могут быть преобразованы в JSON
|
||||
"""
|
||||
def format_json_string(json_string):
|
||||
"""Вспомогательная функция для форматирования строки JSON.
|
||||
|
||||
|
||||
Args:
|
||||
json_string: сырая строка с JSON-данными
|
||||
|
||||
|
||||
Returns:
|
||||
str: отформатированная строка JSON
|
||||
"""
|
||||
substrings = json_string.splitlines()
|
||||
formatted_string_list = []
|
||||
last_substring = substrings.pop()
|
||||
|
||||
for substring in substrings:
|
||||
|
||||
for substring in substrings:
|
||||
if substring.find(':') == -1:
|
||||
if substring == '{':
|
||||
formatted_string_list.append(substring)
|
||||
|
|
@ -63,38 +62,38 @@ class JsonContainerComponent(BaseComponent):
|
|||
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)
|
||||
|
||||
|
||||
# Чтение JSON-содержимого из рабочей области
|
||||
loc = self.get_locator(locator)
|
||||
json_string = loc.inner_text()
|
||||
formatted_json_string = format_json_string(json_string)
|
||||
return json.loads(formatted_json_string)
|
||||
|
||||
return json.loads(formatted_json_string)
|
||||
|
||||
# Проверки:
|
||||
def check_json_equals(self, actual, expected, msg):
|
||||
"""Сравнение JSON-объектов на идентичность.
|
||||
|
||||
|
||||
Args:
|
||||
actual: фактический JSON-объект
|
||||
expected: ожидаемый JSON-объект
|
||||
msg: сообщение об ошибке
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: если объекты не идентичны
|
||||
"""
|
||||
diff = jsondiff.diff(expected, actual, syntax='symmetric')
|
||||
assert len(diff) == 0, f"{msg}. DIFF is {diff}"
|
||||
assert len(diff) == 0, f"{msg}. DIFF is {diff}"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
from playwright.sync_api import Page
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from elements.button_element import Button
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
|
||||
from tools.logger import get_logger
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
from elements.button_element import Button
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("MODAL_WINDOW")
|
||||
|
||||
|
|
@ -23,81 +21,81 @@ class ModalWindowComponent(BaseComponent):
|
|||
def add_content_item(self, name: str, item: object) -> None:
|
||||
"""Добавление элемента содержимого в окно."""
|
||||
self.content_items[name] = item
|
||||
|
||||
|
||||
def get_content_item(self, name: str) -> object | None:
|
||||
"""Получение элемента содержимого по имени."""
|
||||
return self.content_items.get(name)
|
||||
|
||||
|
||||
def add_toolbar_title(self, title: str) -> None:
|
||||
"""Добавление заголовка в панель инструментов."""
|
||||
self.toolbar.add_title(title)
|
||||
|
||||
|
||||
def add_toolbar_button(self, locator: str, name: str) -> None:
|
||||
"""Добавление кнопки в панель инструментов."""
|
||||
self.toolbar.add_button(locator, name)
|
||||
|
||||
|
||||
def add_button(self, locator: str, name: str) -> None:
|
||||
"""Добавление кнопки в окно."""
|
||||
self.buttons.append(Button(self.page, locator, name))
|
||||
|
||||
|
||||
def get_button_by_name(self, name: str) -> Button | None:
|
||||
"""Поиск кнопки по имени."""
|
||||
for button in self.buttons:
|
||||
if button.name == name:
|
||||
return button
|
||||
return None
|
||||
|
||||
|
||||
def click_button(self, name: str) -> None:
|
||||
"""Нажатие кнопки по имени."""
|
||||
button = self.get_button_by_name(name)
|
||||
if button is None:
|
||||
assert False, f"Button with name '{name}' not found"
|
||||
button.click()
|
||||
|
||||
|
||||
def click_toolbar_close_button(self) -> None:
|
||||
"""Нажатие кнопки закрытия в панели инструментов."""
|
||||
self.toolbar.click_button("close")
|
||||
|
||||
|
||||
def scroll_window_down(self) -> None:
|
||||
"""Прокрутка содержимого окна вниз."""
|
||||
self.scroll_down(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
def scroll_window_up(self) -> None:
|
||||
"""Прокрутка содержимого окна вверх."""
|
||||
self.scroll_up(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
def scroll_window_left(self) -> None:
|
||||
"""Прокрутка содержимого окна влево."""
|
||||
self.scroll_left(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
def scroll_window_right(self) -> None:
|
||||
"""Прокрутка содержимого окна вправо."""
|
||||
self.scroll_right(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
# Проверки:
|
||||
def check_window_vertical_scrolling(self) -> bool:
|
||||
"""Проверка возможности вертикальной прокрутки."""
|
||||
return self.is_scrollable_vertically(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
def check_window_horizontal_scrolling(self) -> bool:
|
||||
"""Проверка возможности горизонтальной прокрутки."""
|
||||
return self.is_scrollable_horizontally(ModalWindowLocators.MODAL_WINDOW)
|
||||
|
||||
|
||||
def check_by_window_title(self) -> None:
|
||||
"""Проверка наличия окна по заголовку."""
|
||||
self.toolbar.check_presence(f"Modal window with '{self.toolbar.title}' is missing")
|
||||
|
||||
self.toolbar.check_toolbar_presence(f"Modal window with '{self.toolbar.title}' is missing")
|
||||
|
||||
def check_button_presence(self, name: str) -> None:
|
||||
"""Проверка наличия кнопки по имени."""
|
||||
button = self.get_button_by_name(name)
|
||||
if button is None:
|
||||
assert False, f"Button with name '{name}' not found"
|
||||
button.check_presence(f"Button with name '{name}' is missing")
|
||||
|
||||
|
||||
def check_toolbar_button_presence(self, name: str) -> None:
|
||||
"""Проверка наличия кнопки в панели инструментов."""
|
||||
self.toolbar.check_button_presence(name)
|
||||
|
||||
|
||||
def check_toolbar_button_tooltip(self, name: str, tooltip: str) -> None:
|
||||
"""Проверка подсказки кнопки в панели инструментов."""
|
||||
self.toolbar.check_button_tooltip(name, tooltip)
|
||||
self.toolbar.check_button_tooltip(name, tooltip)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
from playwright.sync_api import Page, Locator
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
from locators.navigation_panel_locators import NavigationPanelLocators
|
||||
|
||||
from tools.logger import get_logger
|
||||
from locators.navigation_panel_locators import NavigationPanelLocators
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("NAVIGATION_PANEL")
|
||||
|
||||
|
|
@ -13,35 +11,46 @@ class NavigationPanelComponent(BaseComponent):
|
|||
|
||||
def __init__(self, page: Page):
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
# Действия:
|
||||
def get_item_names(self, locator: str | Locator) -> list[str]:
|
||||
"""Получает тексты всех элементов по указанному локатору."""
|
||||
loc = self.get_locator(locator)
|
||||
return loc.all_inner_texts()
|
||||
|
||||
|
||||
def click_item(self, locator: str | Locator, item_name: str) -> None:
|
||||
"""Кликает по элементу с указанным текстом."""
|
||||
loc = self.get_locator(locator)
|
||||
loc.get_by_text(item_name).click()
|
||||
|
||||
|
||||
def click_sub_item(self, locator: str | Locator, sublevel_number: int, item_name: str) -> None:
|
||||
"""Кликает по вложенному элементу с указанным текстом."""
|
||||
root_locator = self.get_locator(NavigationPanelLocators.NODE_ROOT)
|
||||
children_locator = self.get_locator(NavigationPanelLocators.NODE_CHILDREN)
|
||||
|
||||
|
||||
loc = self.get_locator(locator)
|
||||
|
||||
|
||||
if sublevel_number == 1:
|
||||
loc.locator(root_locator).get_by_text(item_name).click()
|
||||
elif sublevel_number == 2:
|
||||
loc.locator(children_locator).locator(root_locator).get_by_text(item_name).click()
|
||||
else:
|
||||
raise ValueError("the navigation panel has two levels of nesting only")
|
||||
|
||||
|
||||
# Проверки:
|
||||
def check_item_visibility(self, locator: str | Locator, item_name: str) -> None:
|
||||
"""Проверяет видимость элемента с указанным текстом."""
|
||||
loc = self.get_locator(locator).get_by_text(item_name)
|
||||
|
||||
msg = f"Navigation panel item '{item_name}' is not visible"
|
||||
self.check_presence(loc, msg)
|
||||
|
||||
## временно: в навигационной панели есть две панели с именем Шаблоны
|
||||
## для их различия добавлены индексы Шаблоны_1 для Настройки/Шаблоны
|
||||
## Шаблоны_2 для Настройки/ZTP/Шаблоны
|
||||
loc = self.get_locator(locator)
|
||||
if item_name == "Шаблоны_1":
|
||||
loc = loc.get_by_text("Шаблоны").first
|
||||
elif item_name == "Шаблоны_2":
|
||||
loc = loc.get_by_text("Шаблоны").nth(1)
|
||||
else:
|
||||
loc = loc.get_by_text(item_name)
|
||||
self.check_presence(loc, msg)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
from playwright.sync_api import Page, expect, Locator
|
||||
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
from tools.logger import get_logger
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("TABLE")
|
||||
|
||||
|
|
@ -12,20 +10,31 @@ class TableComponent(BaseComponent):
|
|||
|
||||
def __init__(self, page: Page):
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
# Действия:
|
||||
def get_row_locator(self, table_locator, row_index) -> Locator | None:
|
||||
"""Конструирует локатор для строки с заданным индексом."""
|
||||
table = self.get_locator(table_locator)
|
||||
|
||||
rows = table.locator("//tbody/tr")
|
||||
|
||||
if row_index in range(rows.count()):
|
||||
return rows.nth(row_index)
|
||||
else:
|
||||
return None
|
||||
|
||||
def read(self, locator: str | Locator) -> list[list[str]]:
|
||||
"""Читает данные из таблицы."""
|
||||
table_data = []
|
||||
|
||||
|
||||
table = self.get_locator(locator)
|
||||
|
||||
|
||||
# Чтение заголовка таблицы
|
||||
header_cells = table.locator("//thead/tr")
|
||||
header_cell_text = header_cells.nth(0).inner_text()
|
||||
header_data = header_cell_text.split('\n')
|
||||
table_data.append(header_data)
|
||||
|
||||
|
||||
# Чтение ячеек таблицы
|
||||
rows = table.locator("//tbody/tr")
|
||||
for i in range(rows.count()):
|
||||
|
|
@ -36,33 +45,33 @@ class TableComponent(BaseComponent):
|
|||
cell_text = cells.nth(j).inner_text()
|
||||
row_data.append(cell_text)
|
||||
table_data.append(row_data)
|
||||
|
||||
|
||||
return table_data
|
||||
|
||||
|
||||
# Проверки:
|
||||
def check_first_row_visibility(self, locator: str | Locator) -> None:
|
||||
"""Проверяет видимость первой строки таблицы."""
|
||||
table = self.get_locator(locator)
|
||||
first_row = table.locator("//tbody/tr").first
|
||||
expect(first_row).to_be_visible(), "The first table row is not visible"
|
||||
|
||||
|
||||
def check_last_row_visibility(self, locator: str | Locator) -> None:
|
||||
"""Проверяет видимость последней строки таблицы."""
|
||||
table = self.get_locator(locator)
|
||||
last_row = table.locator("//tbody/tr").last
|
||||
expect(last_row).to_be_visible(), "The last table row is not visible"
|
||||
|
||||
|
||||
def check_row_highlighting(self, locator: str | Locator, row_index: int) -> None:
|
||||
"""Проверяет подсветку строки при наведении."""
|
||||
table = self.get_locator(locator)
|
||||
row = table.locator("//tbody/tr").nth(row_index)
|
||||
|
||||
|
||||
row.scroll_into_view_if_needed()
|
||||
hover_element = row.locator(".body-row-hover")
|
||||
initial_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
||||
|
||||
|
||||
row.hover()
|
||||
self.page.wait_for_timeout(300)
|
||||
|
||||
|
||||
new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
|
||||
assert initial_color == new_color, "Color of row did not change when hovering the cursor"
|
||||
assert initial_color != new_color, "Color of row did not change when hovering the cursor"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from playwright.sync_api import Page, expect, Locator
|
||||
from components.base_component import BaseComponent
|
||||
from elements.tooltip_button_element import TooltipButton
|
||||
from locators.toolbar_locators import ToolbarLocators
|
||||
from tools.logger import get_logger
|
||||
from locators.toolbar_locators import ToolbarLocators
|
||||
from elements.tooltip_button_element import TooltipButton
|
||||
from components.base_component import BaseComponent
|
||||
|
||||
logger = get_logger("TOOLBAR")
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ class ToolbarComponent(BaseComponent):
|
|||
raise AssertionError(f"Unsupported button name {name}")
|
||||
return button.is_not_present(timeout=1000) # Ожидание 1 секунда
|
||||
|
||||
def check_presence(self, message: str) -> None:
|
||||
def check_toolbar_presence(self, message: str) -> None:
|
||||
"""Проверяет видимость тулбара.
|
||||
|
||||
Args:
|
||||
|
|
@ -141,4 +141,4 @@ class ToolbarComponent(BaseComponent):
|
|||
button = self.get_button_by_name(name)
|
||||
if button is None:
|
||||
raise AssertionError(f"Unsupported button name {name}")
|
||||
button.check_tooltip_with_text(ToolbarLocators.TOOLTIP, tooltip)
|
||||
button.check_tooltip_with_text(ToolbarLocators.TOOLTIP, tooltip)
|
||||
|
|
|
|||
25
conftest.py
25
conftest.py
|
|
@ -1,8 +1,7 @@
|
|||
from dotenv import load_dotenv
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
import os
|
||||
import inspect
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
|
@ -14,17 +13,17 @@ def pytest_sessionfinish(session, exitstatus):
|
|||
"""Генерация Markdown файлов с группировкой по классам"""
|
||||
if session.config.getoption("--generate-md"):
|
||||
tests_by_file = {}
|
||||
|
||||
|
||||
for item in session.items:
|
||||
if not (hasattr(item, 'function') and item.function.__doc__):
|
||||
continue
|
||||
|
||||
|
||||
file_path = str(item.fspath)
|
||||
file_name = os.path.splitext(os.path.basename(file_path))[0]
|
||||
|
||||
|
||||
if file_name not in tests_by_file:
|
||||
tests_by_file[file_name] = {}
|
||||
|
||||
|
||||
# Группируем тесты по классам
|
||||
class_name = item.cls.__name__ if hasattr(item, 'cls') else "Без класса"
|
||||
if class_name not in tests_by_file[file_name]:
|
||||
|
|
@ -33,26 +32,26 @@ def pytest_sessionfinish(session, exitstatus):
|
|||
'tests': []
|
||||
}
|
||||
tests_by_file[file_name][class_name]['tests'].append(item)
|
||||
|
||||
|
||||
# Создаем директорию docs
|
||||
output_dir = Path("docs")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
# Генерируем файлы
|
||||
for file_name, classes in tests_by_file.items():
|
||||
md_content = f"# Документация тестов ({file_name}.py)\n\n"
|
||||
|
||||
|
||||
for class_name, class_data in classes.items():
|
||||
if class_name != "Без класса":
|
||||
md_content += f"## Класс: {class_name}\n"
|
||||
if class_data['doc']:
|
||||
md_content += f"{class_data['doc']}\n\n"
|
||||
|
||||
|
||||
for item in class_data['tests']:
|
||||
md_content += f"### {item.name}\n"
|
||||
md_content += f"**Маркеры:** {', '.join(mark.name for mark in item.own_markers)}\n\n"
|
||||
md_content += f"```python\n{inspect.cleandoc(item.function.__doc__)}\n```\n\n"
|
||||
|
||||
|
||||
output_file = output_dir / f"{file_name}.md"
|
||||
output_file.write_text(md_content, encoding='utf-8')
|
||||
|
||||
|
|
@ -62,4 +61,4 @@ def pytest_addoption(parser):
|
|||
action="store_true",
|
||||
default=False,
|
||||
help="Генерировать Markdown документацию с группировкой по классам"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
|
||||
|
||||
class Constants:
|
||||
"""Класс для хранения констант и переменных окружения.
|
||||
|
||||
|
|
@ -16,4 +15,4 @@ class Constants:
|
|||
login = os.getenv('AUTH_LOGIN')
|
||||
password = os.getenv('AUTH_PASSWORD')
|
||||
except KeyError:
|
||||
print("LOGIN OR PASSWORD WASN'T FOUND")
|
||||
print("LOGIN OR PASSWORD WASN'T FOUND")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
from typing import Dict, Optional
|
||||
|
||||
from typing import Dict
|
||||
|
||||
class Environment:
|
||||
"""Класс для работы с окружением и URL-адресами."""
|
||||
|
|
@ -29,20 +28,20 @@ class Environment:
|
|||
return self.URLS[self.env] + "e-nms-ui/"
|
||||
return self.URLS[self.env]
|
||||
raise Exception(f"Unknown value of ENV variable {self.env}")
|
||||
|
||||
|
||||
def get_request_url(self) -> str:
|
||||
"""Возвращает URL для API-запросов."""
|
||||
if self.env in self.URLS:
|
||||
return self.URLS[self.env]
|
||||
raise Exception(f"Unknown value of ENV variable {self.env}")
|
||||
|
||||
|
||||
def set_access_token(self, token: str) -> None:
|
||||
"""Устанавливает токен доступа."""
|
||||
self.token = token
|
||||
|
||||
|
||||
def get_access_token(self) -> str:
|
||||
"""Возвращает текущий токен доступа."""
|
||||
return self.token
|
||||
|
||||
|
||||
host: Environment = Environment()
|
||||
host: Environment = Environment()
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ roles_dict = {
|
|||
"operator": "Оператор",
|
||||
"inform_secur_user": "Специалист информационной безопасности",
|
||||
"user": "Пользователь"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
from playwright.sync_api import Page, Locator, expect, TimeoutError
|
||||
from typing import Optional
|
||||
|
||||
from tools.logger import get_logger
|
||||
|
||||
logger = get_logger("BASE_ELEMENT")
|
||||
|
||||
|
||||
class BaseElement:
|
||||
"""Базовый класс для работы с элементами страницы."""
|
||||
|
||||
|
|
@ -27,29 +24,29 @@ class BaseElement:
|
|||
|
||||
# Действия:
|
||||
def click(self) -> None:
|
||||
logger.info(f'Clicking {self.type_of} "{self.name}"')
|
||||
logger.info(f"Clicking {self.type_of} '{self.name}'")
|
||||
self.locator.click()
|
||||
|
||||
def get_text(self, index: int) -> str:
|
||||
logger.info(f'Get text for {self.type_of} "{self.name}"')
|
||||
logger.info(f"Get text for {self.type_of} '{self.name}'")
|
||||
return self.locator.nth(index).text_content()
|
||||
|
||||
def wait_for_element(self, timeout: int = 12000) -> None:
|
||||
logger.info(f'Wait for {self.type_of} "{self.name}"')
|
||||
logger.info(f"Wait for {self.type_of} '{self.name}'")
|
||||
self.locator.wait_for(timeout=timeout)
|
||||
|
||||
# Проверки:
|
||||
def check_have_text(self, text: str, msg: str) -> None:
|
||||
logger.info(f'Check that {self.type_of} "{self.name}" has text "{text}"')
|
||||
logger.info(f"Check that {self.type_of} '{self.name}' has text '{text}'")
|
||||
expect(self.locator).to_have_text(text), msg
|
||||
|
||||
def check_presence(self, msg: str) -> None:
|
||||
logger.info(f'Check that {self.type_of} "{self.name}" is present')
|
||||
logger.info(f"Check that {self.type_of} '{self.name}' is present")
|
||||
print(self.locator)
|
||||
expect(self.locator).to_be_visible(visible=True, timeout=12000), msg
|
||||
|
||||
def is_present(self, timeout: int = 5000) -> bool:
|
||||
logger.info(f'Check that {self.type_of} "{self.name}" is present')
|
||||
logger.info(f"Check that {self.type_of} '{self.name}' is present")
|
||||
try:
|
||||
self.locator.wait_for(timeout=timeout)
|
||||
except TimeoutError:
|
||||
|
|
@ -57,9 +54,9 @@ class BaseElement:
|
|||
return True
|
||||
|
||||
def is_not_present(self, timeout: int = 5000) -> bool:
|
||||
logger.info(f'Check that {self.type_of} "{self.name}" is missing')
|
||||
logger.info(f"Check that {self.type_of} '{self.name}' is missing")
|
||||
try:
|
||||
self.locator.wait_for(timeout=timeout)
|
||||
except TimeoutError:
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from playwright.sync_api import expect
|
||||
|
||||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("BUTTON")
|
||||
|
||||
|
|
@ -25,4 +23,4 @@ class Button(BaseElement):
|
|||
# (Методы действий будут добавлены по мере необходимости)
|
||||
|
||||
# Проверки:
|
||||
# (Методы проверок будут добавлены по мере необходимости)
|
||||
# (Методы проверок будут добавлены по мере необходимости)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("CHECKBOX")
|
||||
|
||||
|
|
@ -22,12 +22,12 @@ class Checkbox(BaseElement):
|
|||
# Действия:
|
||||
def check(self) -> None:
|
||||
"""Устанавливает чекбокс в отмеченное состояние."""
|
||||
logger.info(f'Checking checkbox "{self.name}"')
|
||||
logger.info(f"Checking checkbox '{self.name}'")
|
||||
self.locator.check()
|
||||
|
||||
def uncheck(self) -> None:
|
||||
"""Снимает отметку с чекбокса."""
|
||||
logger.info(f'Unchecking checkbox "{self.name}"')
|
||||
logger.info(f"Unchecking checkbox '{self.name}'")
|
||||
self.locator.uncheck()
|
||||
|
||||
# Проверки:
|
||||
|
|
@ -37,5 +37,5 @@ class Checkbox(BaseElement):
|
|||
Returns:
|
||||
True, если чекбокс отмечен, иначе False.
|
||||
"""
|
||||
logger.info(f'Checking if checkbox "{self.name}" is checked')
|
||||
return self.locator.is_checked()
|
||||
logger.info(f"Checking if checkbox '{self.name}' is checked")
|
||||
return self.locator.is_checked()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
from playwright.sync_api import expect
|
||||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("DROPDOWN_LIST")
|
||||
|
||||
|
||||
class DropdownList(BaseElement):
|
||||
"""Класс для работы с выпадающими списками на странице.
|
||||
|
||||
|
|
@ -43,4 +41,4 @@ class DropdownList(BaseElement):
|
|||
logger.info(f'Checking item with text "{text}" in dropdown "{self.name}"')
|
||||
enabled = self.page.get_by_role("listitem").filter(has_text=text).is_enabled()
|
||||
if not enabled:
|
||||
assert False, f"Dropdown list item '{text}' is missing or disabled"
|
||||
assert False, f"Dropdown list item '{text}' is missing or disabled"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("TEXT")
|
||||
|
||||
|
||||
class Text(BaseElement):
|
||||
"""Класс для работы с текстовыми элементами на странице.
|
||||
|
||||
|
|
@ -23,4 +22,4 @@ class Text(BaseElement):
|
|||
# (Методы действий будут добавлены по мере необходимости)
|
||||
|
||||
# Проверки:
|
||||
# (Методы проверок будут добавлены по мере необходимости)
|
||||
# (Методы проверок будут добавлены по мере необходимости)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from playwright.sync_api import expect
|
||||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("TEXT_INPUT")
|
||||
|
||||
|
|
@ -56,4 +56,4 @@ class TextInput(BaseElement):
|
|||
AssertionError: Если поле не пустое.
|
||||
"""
|
||||
logger.info(f'Checking that text input "{self.name}" is empty')
|
||||
expect(self.locator).to_be_empty(), msg
|
||||
expect(self.locator).to_be_empty(), msg
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from elements.base_element import BaseElement
|
||||
from tools.logger import get_logger
|
||||
from elements.base_element import BaseElement
|
||||
|
||||
logger = get_logger("TOOLTIP_BUTTON")
|
||||
|
||||
|
||||
class TooltipButton(BaseElement):
|
||||
"""Класс элемента кнопки с всплывающей подсказкой.
|
||||
|
||||
|
|
@ -31,13 +30,13 @@ class TooltipButton(BaseElement):
|
|||
"""
|
||||
# Наведение на элемент для отображения подсказки
|
||||
self.locator.hover()
|
||||
|
||||
|
||||
# Получение элемента подсказки
|
||||
tooltip = self.page.locator(tooltip_locator)
|
||||
|
||||
|
||||
# Проверка соответствия текста
|
||||
actual_text = tooltip.text_content().strip()
|
||||
assert actual_text == expected_text, (
|
||||
f"Текст подсказки не соответствует ожидаемому. "
|
||||
f"Ожидалось: '{expected_text}', получено: '{actual_text}'"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import pytest
|
||||
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright, Playwright
|
||||
import os
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
|
||||
|
|
@ -24,20 +23,24 @@ def pytest_addoption(parser: Parser):
|
|||
--t: Таймаут по умолчанию (мс)
|
||||
--l: Локаль браузера
|
||||
"""
|
||||
parser.addoption('--bn', action='store', default="chrome",
|
||||
parser.addoption('--bn', action='store', default="chrome",
|
||||
help="Choose browser: chrome, remote_chrome or firefox")
|
||||
parser.addoption('--h', action='store', default=False,
|
||||
parser.addoption('--h', action='store', default=False,
|
||||
help='Choose headless: True or False')
|
||||
parser.addoption('--s', action='store', default={'width': 1600, 'height': 900},
|
||||
parser.addoption('--s', action='store', default={'width': 1600, 'height': 900},
|
||||
help='Size window: width,height')
|
||||
# Закомментированные альтернативные размеры окон
|
||||
# parser.addoption('--s', action='store', default={'width': 1920, 'height': 1080}, 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,
|
||||
# parser.addoption('--s', action='store', default={'width': 1920, 'height': 1080},
|
||||
# help='Size window: width,height')
|
||||
# parser.addoption('--s', action='store', default={'width': 1920, 'height': 300},
|
||||
# help='Size window: width,height')
|
||||
# parser.addoption('--s', action='store', default={'width': 300, 'height': 420},
|
||||
# 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,
|
||||
parser.addoption('--t', action='store', default=60000,
|
||||
help='Choose timeout')
|
||||
parser.addoption('--l', action='store', default='ru-RU',
|
||||
parser.addoption('--l', action='store', default='ru-RU',
|
||||
help='Choose locale')
|
||||
# Закомментированная опция для Qase
|
||||
# parser.addini('qs_to_api_token', default=os.getenv("QASE_TOKEN"), help='Qase app token')
|
||||
|
|
@ -60,7 +63,7 @@ def browser(request: FixtureRequest) -> Page:
|
|||
Автоматически закрывает браузер и контексты после завершения тестов.
|
||||
"""
|
||||
playwright = sync_playwright().start()
|
||||
|
||||
|
||||
# Выбор браузера на основе параметра командной строки
|
||||
if request.config.getoption("bn") == 'remote_chrome':
|
||||
browser = get_remote_chrome(playwright, request)
|
||||
|
|
@ -78,9 +81,9 @@ def browser(request: FixtureRequest) -> Page:
|
|||
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()
|
||||
|
|
@ -177,8 +180,8 @@ def get_context(browser: Browser, request: FixtureRequest, start: str) -> Browse
|
|||
@pytest.fixture(scope="function")
|
||||
def return_back(browser: Page):
|
||||
"""Фикстура для возврата на предыдущую страницу в браузере.
|
||||
|
||||
|
||||
Args:
|
||||
browser: Экземпляр страницы браузера.
|
||||
"""
|
||||
browser.go_back()
|
||||
browser.go_back()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
class ButtonLocators:
|
||||
BUTTON_LICENSE_UPDATE = "//div[@class='scrollarea__footer']//button"
|
||||
|
||||
|
||||
|
||||
|
||||
TOOLTIP = "//div[contains(@class,'v-tooltip__content menuable__content__active')]"
|
||||
BUTTON_DELETE_SESSION = "button.v-btn--icon svg[fill='#4caf50']"
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ class ConfirmLocators:
|
|||
CONFIRM = "//div[contains(@class, 'v-dialog--active')]"
|
||||
TITLE = "//div[@class='v-card__title']/h3"
|
||||
BUTTON_CLOSE = "//div[@class='vuedl-layout__closeBtn']"
|
||||
TEXT = f"{CONFIRM}/div[2]/div[@class='v-card__text']"
|
||||
TEXT = f"{CONFIRM}/div[2]/div[@class='v-card__text']"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ class EventPanelLocators:
|
|||
BUTTONS_BLOCK (str): XPath локатор блока кнопок в панели инструментов.
|
||||
Находится во втором блоке элементов toolbar'а внутри контентной области.
|
||||
"""
|
||||
BUTTONS_BLOCK = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][2]"
|
||||
BUTTONS_BLOCK = "//nav/div[@class='v-toolbar__content']/div[@class='v-toolbar__items'][2]"
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ class InputLocators:
|
|||
- Контейнер поля ввода (v-input__control)
|
||||
- Непосредственно текстовое поле (textarea)
|
||||
"""
|
||||
LICENSE_ID_UPDATE = "//div[@class='scrollarea__footer']//div[@class='v-input__control']//textarea"
|
||||
LICENSE_ID_UPDATE = "//div[@class='scrollarea__footer']//div[@class='v-input__control']//textarea"
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ class JsonContainerLocators:
|
|||
Ищет div с классом, содержащим 'scrollarea__body'.
|
||||
"""
|
||||
CONTAINER = "//div[contains(@class,'jv-container')]"
|
||||
SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body')]"
|
||||
SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body')]"
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ class ModalWindowLocators:
|
|||
Атрибуты:
|
||||
MODAL_WINDOW (str): XPath локатор активного модального окна.
|
||||
INPUT_FORM_USER_DATA (str): XPath локатор формы для ввода пользовательских данных.
|
||||
TEXT_FIELD_INPUT_FORM_USER_DATA (str): Относительный XPath текстового поля ввода
|
||||
TEXT_FIELD_INPUT_FORM_USER_DATA (str): Относительный XPath текстового поля ввода
|
||||
внутри формы пользовательских данных.
|
||||
ROLES_FIELD_INPUT_FORM_USER_DATA (str): Относительный XPath поля выбора ролей
|
||||
ROLES_FIELD_INPUT_FORM_USER_DATA (str): Относительный XPath поля выбора ролей
|
||||
внутри формы пользовательских данных.
|
||||
ROLES_MENU_INPUT_FORM_USER_DATA (str): XPath локатор активного меню выбора ролей.
|
||||
LABEL_INPUT_FORM_USER_DATA (str): XPath локатор метки поля ввода в форме.
|
||||
"""
|
||||
MODAL_WINDOW = "//div[contains(@class, 'v-dialog--active')]"
|
||||
|
||||
|
||||
INPUT_FORM_USER_DATA = "//form[@class='v-form']"
|
||||
TEXT_FIELD_INPUT_FORM_USER_DATA = "xpath=div[2]/div/div/div/div/input"
|
||||
ROLES_FIELD_INPUT_FORM_USER_DATA = "xpath=div[2]/div/div/div/div/div[1]"
|
||||
ROLES_MENU_INPUT_FORM_USER_DATA = "//div[contains(@class, 'menuable__content__active')]"
|
||||
LABEL_INPUT_FORM_USER_DATA = "//label[contains(@class,'v-label')]/span"
|
||||
LABEL_INPUT_FORM_USER_DATA = "//label[contains(@class,'v-label')]/span"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ class NavigationPanelLocators:
|
|||
"""
|
||||
PANEL_MAIN = "//ul[contains(@class, 'v-expansion-panel')]"
|
||||
PANEL_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//ul[contains(@class, 'v-expansion-panel')]]"
|
||||
|
||||
|
||||
NODE_ROOT = "//div[contains(@class,'v-treeview-node__root')]"
|
||||
NODE_CHILDREN = "//div[contains(@class,'v-treeview-node__children')]"
|
||||
NODE_CHILDREN = "//div[contains(@class,'v-treeview-node__children')]"
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ class TableLocators:
|
|||
содержащего таблицу с классом scrolltable__container.
|
||||
"""
|
||||
TABLE_WORK_AREA = "//div[@class='scrollarea__body']/div/div/div/table"
|
||||
TABLE_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//table[@class='scrolltable__container']]//tbody"
|
||||
TABLE_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//table[@class='scrolltable__container']]//tbody"
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ class TextLocators:
|
|||
Ищет span с классами 'title' и 'text_select' (выделяемый текст).
|
||||
"""
|
||||
TITLE_LICENSE_INPUT_FORM = "//span[@class='title']"
|
||||
LICENSE_ID = "//span[@class='title text_select']"
|
||||
LICENSE_ID = "//span[@class='title text_select']"
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ class ToolbarLocators:
|
|||
- 'menuable__content__active' (показанное состояние)
|
||||
"""
|
||||
TITLE = "//nav//div[contains(@class, 'v-toolbar__title')]"
|
||||
TOOLTIP = "//div[contains(@class,'v-tooltip__content menuable__content__active')]"
|
||||
TOOLTIP = "//div[contains(@class,'v-tooltip__content menuable__content__active')]"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
from playwright.sync_api import Page
|
||||
from components.confirm_component import ConfirmComponent
|
||||
from components.modal_window_component import ModalWindowComponent
|
||||
from elements.checkbox_element import Checkbox
|
||||
from elements.dropdown_list_element import DropdownList
|
||||
from elements.text_element import Text
|
||||
from elements.text_input_element import TextInput
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
from data.roles_dict import roles_dict
|
||||
import re
|
||||
from playwright.sync_api import Page
|
||||
from tools.logger import get_logger
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
from elements.text_input_element import TextInput
|
||||
from elements.text_element import Text
|
||||
from elements.dropdown_list_element import DropdownList
|
||||
from elements.checkbox_element import Checkbox
|
||||
from data.roles_dict import roles_dict
|
||||
from components.modal_window_component import ModalWindowComponent
|
||||
from components.confirm_component import ConfirmComponent
|
||||
|
||||
|
||||
logger = get_logger("ADD_USER_MODAL_WINDOW")
|
||||
|
||||
|
|
@ -29,87 +30,87 @@ class AddUserModalWindow(ModalWindowComponent):
|
|||
def __init__(self, page: Page):
|
||||
"""Инициализация компонентов модального окна добавления пользователя."""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
# Локаторы элементов формы
|
||||
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
|
||||
roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA
|
||||
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
|
||||
label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA
|
||||
roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA
|
||||
|
||||
|
||||
# Настройка заголовка и кнопки закрытия тулбара
|
||||
self.window_title = "Добавить нового пользователя"
|
||||
locator_button_toolbar_close = self.page.get_by_role("navigation").filter(
|
||||
has_text=re.compile(self.window_title)
|
||||
).get_by_role("button")
|
||||
|
||||
|
||||
self.add_toolbar_title(self.window_title)
|
||||
self.add_toolbar_button(locator_button_toolbar_close, "close")
|
||||
|
||||
|
||||
# Добавление элементов формы
|
||||
checkbox_1 = Checkbox(
|
||||
page,
|
||||
self.page.get_by_role("checkbox").nth(0),
|
||||
page,
|
||||
self.page.get_by_role("checkbox").nth(0),
|
||||
"active_directory"
|
||||
)
|
||||
self.add_content_item("active_directory_checkbox", checkbox_1)
|
||||
|
||||
|
||||
label_1 = Text(
|
||||
page,
|
||||
self.page.locator(label_locator).nth(0),
|
||||
page,
|
||||
self.page.locator(label_locator).nth(0),
|
||||
"active_directory_checkbox_label"
|
||||
)
|
||||
self.add_content_item("active_directory_checkbox_label", label_1)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[2]").locator(text_field_locator)
|
||||
type_auth_input = TextInput(page, loc, "type_auth_input")
|
||||
self.add_content_item("type_auth_input", type_auth_input)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[3]").locator(text_field_locator)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[2]").locator(text_field_locator)
|
||||
name_input = TextInput(page, loc, "name_input")
|
||||
self.add_content_item("name_input", name_input)
|
||||
|
||||
role_loc = self.page.locator(input_form_locator).locator("xpath=div[4]").locator(roles_field_locator)
|
||||
|
||||
role_loc = self.page.locator(input_form_locator).locator("xpath=div[3]").locator(roles_field_locator)
|
||||
role_input = TextInput(page, role_loc, "role_input")
|
||||
self.add_content_item("role_input", role_input)
|
||||
self.add_content_item(
|
||||
"roles_list",
|
||||
"roles_list",
|
||||
DropdownList(page, roles_menu_locator, "roles_list")
|
||||
)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[4]").locator(text_field_locator)
|
||||
password_input = TextInput(page, loc, "password_input")
|
||||
self.add_content_item("password_input", password_input)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[5]").locator(text_field_locator)
|
||||
commentary_input = TextInput(page, loc, "commentary_input")
|
||||
self.add_content_item("commentary_input", commentary_input)
|
||||
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[6]").locator(text_field_locator)
|
||||
email_input = TextInput(page, loc, "email_input")
|
||||
self.add_content_item("email_input", email_input)
|
||||
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[7]").locator(text_field_locator)
|
||||
phone_input = TextInput(page, loc, "phone_input")
|
||||
self.add_content_item("phone_input", phone_input)
|
||||
|
||||
|
||||
checkbox_2 = Checkbox(
|
||||
page,
|
||||
page.get_by_role("checkbox").nth(1),
|
||||
page,
|
||||
page.get_by_role("checkbox").nth(1),
|
||||
"push_notification"
|
||||
)
|
||||
self.add_content_item("push_notification_checkbox", checkbox_2)
|
||||
|
||||
|
||||
label_2 = Text(
|
||||
page,
|
||||
self.page.locator(label_locator).nth(1),
|
||||
page,
|
||||
self.page.locator(label_locator).nth(1),
|
||||
"push_notification_checkbox_label"
|
||||
)
|
||||
self.add_content_item("push_notification_checkbox_label", label_2)
|
||||
|
||||
|
||||
# Добавление кнопок действий
|
||||
locator_button_add = self.page.get_by_role("button", name="Добавить")
|
||||
self.add_button(locator_button_add, "add")
|
||||
|
||||
|
||||
locator_button_close = self.page.get_by_role("button", name="Закрыть")
|
||||
self.add_button(locator_button_close, "close")
|
||||
|
||||
|
||||
self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ")
|
||||
|
||||
def new_user(self, user_data):
|
||||
|
|
@ -118,9 +119,9 @@ class AddUserModalWindow(ModalWindowComponent):
|
|||
Args:
|
||||
user_data (dict): Словарь с данными пользователя. Может содержать ключи:
|
||||
- active_directory_checked (bool): Состояние чекбокса Active Directory
|
||||
- type_auth (str): Тип авторизации
|
||||
- name (str): Имя пользователя
|
||||
- role (str): Роль пользователя
|
||||
- password (str): Пароль пользователя
|
||||
- commentary (str): Комментарий
|
||||
- email (str): Email
|
||||
- phone_number (str): Номер телефона
|
||||
|
|
@ -130,57 +131,57 @@ class AddUserModalWindow(ModalWindowComponent):
|
|||
AssertionError: Если подтверждающее окно не отображается
|
||||
"""
|
||||
fields = user_data.keys()
|
||||
|
||||
|
||||
if "active_directory_checked" in fields:
|
||||
checkbox = self.get_content_item("active_directory_checkbox")
|
||||
if user_data["active_directory_checked"]:
|
||||
checkbox.check()
|
||||
else:
|
||||
checkbox.uncheck()
|
||||
|
||||
if "type_auth" in fields:
|
||||
input_field = self.get_content_item("type_auth_input")
|
||||
input_field.input_value(user_data["type_auth"])
|
||||
|
||||
|
||||
if "name" in fields:
|
||||
input_field = self.get_content_item("name_input")
|
||||
input_field.input_value(user_data["name"])
|
||||
|
||||
|
||||
if "role" in fields:
|
||||
role_field = self.get_content_item("role_input")
|
||||
role_field.click()
|
||||
|
||||
|
||||
roles_list = self.get_content_item("roles_list")
|
||||
roles_list.check_item_with_text(user_data["role"])
|
||||
roles_list.click_item_with_text(user_data["role"])
|
||||
|
||||
if "commentary" in fields:
|
||||
|
||||
if "password" in fields:
|
||||
input_field = self.get_content_item("password_input")
|
||||
input_field.input_value(user_data["password"])
|
||||
|
||||
if "commentary" in fields:
|
||||
input_field = self.get_content_item("commentary_input")
|
||||
input_field.input_value(user_data["commentary"])
|
||||
|
||||
if "email" in fields:
|
||||
input_field.input_value(user_data["commentary"])
|
||||
|
||||
if "email" in fields:
|
||||
input_field = self.get_content_item("email_input")
|
||||
input_field.input_value(user_data["email"])
|
||||
|
||||
if "phone_number" in fields:
|
||||
input_field.input_value(user_data["email"])
|
||||
|
||||
if "phone_number" in fields:
|
||||
input_field = self.get_content_item("phone_input")
|
||||
input_field.input_value(user_data["phone_number"])
|
||||
|
||||
|
||||
if "push_notification_checked" in fields:
|
||||
checkbox = self.get_content_item("push_notification_checkbox")
|
||||
if user_data["push_notification_checked"]:
|
||||
checkbox.check()
|
||||
else:
|
||||
checkbox.uncheck()
|
||||
|
||||
|
||||
# Отправка формы
|
||||
add_button = self.get_button_by_name("add")
|
||||
add_button.click()
|
||||
|
||||
|
||||
# Подтверждение действия
|
||||
title = "Добавить нового пользователя"
|
||||
self.new_user_confirm.check_title(
|
||||
title,
|
||||
title,
|
||||
f"Confirmation dialog window with title '{title}' is missing"
|
||||
)
|
||||
self.new_user_confirm.click_allow_button()
|
||||
|
|
@ -189,41 +190,44 @@ class AddUserModalWindow(ModalWindowComponent):
|
|||
"""Закрывает модальное окно с помощью кнопки 'Закрыть'."""
|
||||
close_button = self.get_button_by_name("close")
|
||||
close_button.click()
|
||||
|
||||
|
||||
def close_window_by_toolbar_button(self):
|
||||
"""Закрывает модальное окно с помощью кнопки закрытия в тулбаре."""
|
||||
self.click_toolbar_close_button()
|
||||
|
||||
|
||||
def check_content(self):
|
||||
"""Проверяет наличие и корректность всех элементов модального окна.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные
|
||||
"""
|
||||
self.check_by_window_title()
|
||||
|
||||
|
||||
self.check_toolbar_button_presence("close")
|
||||
self.check_toolbar_button_tooltip("close", "Закрыть")
|
||||
|
||||
|
||||
for name in self.content_items.keys():
|
||||
item = self.get_content_item(name)
|
||||
|
||||
|
||||
if name == "active_directory_checkbox_label":
|
||||
item.check_have_text(
|
||||
"Active Directory",
|
||||
"Active Directory",
|
||||
"Label 'Active Directory' is missing"
|
||||
)
|
||||
elif name == "push_notification_checkbox_label":
|
||||
item.check_have_text(
|
||||
"Подписка на Push-уведомления",
|
||||
"Подписка на Push-уведомления",
|
||||
"Label 'Подписка на Push-уведомления' is missing"
|
||||
)
|
||||
elif name == "role_input":
|
||||
item.click()
|
||||
roles_list = self.get_content_item("roles_list")
|
||||
roles_list.check_presence("Roles list is missing")
|
||||
|
||||
|
||||
for role in roles_dict.values():
|
||||
# временно, пока есть несоответствие со списком ролей в вкладке Сессии
|
||||
if role == "Пользователь":
|
||||
continue
|
||||
roles_list.check_item_with_text(role)
|
||||
elif name == "roles_list":
|
||||
continue
|
||||
|
|
@ -231,6 +235,6 @@ class AddUserModalWindow(ModalWindowComponent):
|
|||
item.check_presence(
|
||||
f"Modal window content item with name '{name}' is missing"
|
||||
)
|
||||
|
||||
|
||||
self.check_button_presence("add")
|
||||
self.check_button_presence("close")
|
||||
self.check_button_presence("close")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from playwright.sync_api import Page
|
||||
from components.confirm_component import ConfirmComponent
|
||||
from components.modal_window_component import ModalWindowComponent
|
||||
from elements.checkbox_element import Checkbox
|
||||
from elements.dropdown_list_element import DropdownList
|
||||
from elements.text_element import Text
|
||||
from elements.text_input_element import TextInput
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
import re
|
||||
from playwright.sync_api import Page
|
||||
from tools.logger import get_logger
|
||||
from locators.modal_window_locators import ModalWindowLocators
|
||||
from elements.text_input_element import TextInput
|
||||
from elements.text_element import Text
|
||||
from elements.dropdown_list_element import DropdownList
|
||||
from elements.checkbox_element import Checkbox
|
||||
from components.modal_window_component import ModalWindowComponent
|
||||
from components.confirm_component import ConfirmComponent
|
||||
|
||||
logger = get_logger("EDIT_USER_MODAL_WINDOW")
|
||||
|
||||
|
|
@ -29,80 +29,76 @@ class EditUserModalWindow(ModalWindowComponent):
|
|||
def __init__(self, page: Page, user_name: str):
|
||||
"""Инициализация компонентов модального окна редактирования пользователя."""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
# Локаторы элементов формы
|
||||
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
|
||||
roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA
|
||||
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
|
||||
label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA
|
||||
roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA
|
||||
|
||||
|
||||
# Настройка заголовка и кнопки закрытия
|
||||
self.window_title = user_name
|
||||
locator_button_toolbar_close = self.page.get_by_role("navigation").filter(
|
||||
has_text=re.compile(self.window_title)
|
||||
).get_by_role("button")
|
||||
|
||||
|
||||
self.add_toolbar_title(self.window_title)
|
||||
self.add_toolbar_button(locator_button_toolbar_close, "close")
|
||||
|
||||
|
||||
# Добавление полей формы
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[1]").locator(text_field_locator)
|
||||
type_auth_input = TextInput(page, loc, "type_auth_input")
|
||||
self.add_content_item("type_auth_input", type_auth_input)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[2]").locator(text_field_locator)
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[1]").locator(text_field_locator)
|
||||
name_input = TextInput(page, loc, "name_input")
|
||||
self.add_content_item("name_input", name_input)
|
||||
|
||||
role_loc = self.page.locator(input_form_locator).locator("xpath=div[3]").locator(roles_field_locator)
|
||||
|
||||
role_loc = self.page.locator(input_form_locator).locator("xpath=div[2]").locator(roles_field_locator)
|
||||
role_input = TextInput(page, role_loc, "role_input")
|
||||
self.add_content_item("role_input", role_input)
|
||||
self.add_content_item(
|
||||
"roles_list",
|
||||
"roles_list",
|
||||
DropdownList(page, roles_menu_locator, "roles_list")
|
||||
)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[4]").locator(text_field_locator)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[3]").locator(text_field_locator)
|
||||
commentary_input = TextInput(page, loc, "commentary_input")
|
||||
self.add_content_item("commentary_input", commentary_input)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[5]").locator(text_field_locator)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[4]").locator(text_field_locator)
|
||||
email_input = TextInput(page, loc, "email_input")
|
||||
self.add_content_item("email_input", email_input)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[6]").locator(text_field_locator)
|
||||
|
||||
loc = self.page.locator(input_form_locator).locator("xpath=div[5]").locator(text_field_locator)
|
||||
phone_input = TextInput(page, loc, "phone_input")
|
||||
self.add_content_item("phone_input", phone_input)
|
||||
|
||||
|
||||
# Добавление чекбоксов и их меток
|
||||
checkbox_2 = Checkbox(
|
||||
page,
|
||||
page.get_by_role("checkbox").nth(0),
|
||||
page,
|
||||
page.get_by_role("checkbox").nth(0),
|
||||
"push_notification"
|
||||
)
|
||||
self.add_content_item("push_notification_checkbox", checkbox_2)
|
||||
|
||||
|
||||
label_2 = Text(
|
||||
page,
|
||||
self.page.locator(label_locator).nth(0),
|
||||
page,
|
||||
self.page.locator(label_locator).nth(0),
|
||||
"push_notification_checkbox_label"
|
||||
)
|
||||
self.add_content_item("push_notification_checkbox_label", label_2)
|
||||
|
||||
|
||||
# Добавление кнопок действий
|
||||
locator_button_save = self.page.get_by_role("button", name="Сохранить")
|
||||
self.add_button(locator_button_save, "save")
|
||||
|
||||
|
||||
locator_button_delete = self.page.get_by_role("button", name="Удалить")
|
||||
self.add_button(locator_button_delete, "delete")
|
||||
|
||||
|
||||
locator_button_reset = self.page.get_by_role("button", name="Сбросить пароль")
|
||||
self.add_button(locator_button_reset, "reset_password")
|
||||
|
||||
|
||||
locator_button_close = self.page.get_by_role("button", name="Закрыть")
|
||||
self.add_button(locator_button_close, "close")
|
||||
|
||||
|
||||
# Инициализация компонентов подтверждения
|
||||
self.save_user_confirm = ConfirmComponent(page, " Отмена ", " Сохранить ")
|
||||
self.delete_user_confirm = ConfirmComponent(page, " Отмена ", " Удалить ")
|
||||
|
|
@ -111,33 +107,32 @@ class EditUserModalWindow(ModalWindowComponent):
|
|||
"""Закрывает модальное окно с помощью кнопки 'Закрыть'."""
|
||||
close_button = self.get_button_by_name("close")
|
||||
close_button.click()
|
||||
|
||||
|
||||
def close_window_by_toolbar_button(self):
|
||||
"""Закрывает модальное окно с помощью кнопки закрытия в тулбаре."""
|
||||
self.click_toolbar_close_button()
|
||||
|
||||
|
||||
def delete_user(self):
|
||||
"""Удаляет пользователя с подтверждением действия.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если окно подтверждения не отображается
|
||||
"""
|
||||
delete_button = self.get_button_by_name("delete")
|
||||
delete_button.click()
|
||||
|
||||
|
||||
title = "Удаление"
|
||||
self.delete_user_confirm.check_title(
|
||||
title,
|
||||
title,
|
||||
f"Confirmation dialog window with title '{title}' is missing"
|
||||
)
|
||||
self.delete_user_confirm.click_allow_button()
|
||||
|
||||
|
||||
def edit_user(self, user_data):
|
||||
"""Редактирует данные пользователя.
|
||||
|
||||
|
||||
Args:
|
||||
user_data (dict): Словарь с обновляемыми данными пользователя. Может содержать:
|
||||
- type_auth (str): Тип авторизации
|
||||
- name (str): Имя пользователя
|
||||
- role (str): Роль пользователя
|
||||
- commentary (str): Комментарий
|
||||
|
|
@ -146,48 +141,44 @@ class EditUserModalWindow(ModalWindowComponent):
|
|||
- push_notification_checked (bool): Состояние чекбокса уведомлений
|
||||
"""
|
||||
fields = user_data.keys()
|
||||
|
||||
if "type_auth" in fields:
|
||||
input_field = self.get_content_item("type_auth_input")
|
||||
input_field.input_value(user_data["type_auth"])
|
||||
|
||||
|
||||
if "name" in fields:
|
||||
input_field = self.get_content_item("name_input")
|
||||
input_field.input_value(user_data["name"])
|
||||
|
||||
|
||||
if "role" in fields:
|
||||
role_field = self.get_content_item("role_input")
|
||||
role_field.click()
|
||||
|
||||
|
||||
roles_list = self.get_content_item("roles_list")
|
||||
roles_list.check_item_with_text(user_data["role"])
|
||||
roles_list.click_item_with_text(user_data["role"])
|
||||
|
||||
if "commentary" in fields:
|
||||
|
||||
if "commentary" in fields:
|
||||
input_field = self.get_content_item("commentary_input")
|
||||
input_field.input_value(user_data["commentary"])
|
||||
|
||||
if "email" in fields:
|
||||
input_field.input_value(user_data["commentary"])
|
||||
|
||||
if "email" in fields:
|
||||
input_field = self.get_content_item("email_input")
|
||||
input_field.input_value(user_data["email"])
|
||||
|
||||
if "phone_number" in fields:
|
||||
input_field.input_value(user_data["email"])
|
||||
|
||||
if "phone_number" in fields:
|
||||
input_field = self.get_content_item("phone_input")
|
||||
input_field.input_value(user_data["phone_number"])
|
||||
|
||||
|
||||
if "push_notification_checked" in fields:
|
||||
checkbox = self.get_content_item("push_notification_checkbox")
|
||||
if user_data["push_notification_checked"]:
|
||||
checkbox.check()
|
||||
else:
|
||||
checkbox.uncheck()
|
||||
|
||||
|
||||
save_button = self.get_button_by_name("save")
|
||||
save_button.click()
|
||||
|
||||
|
||||
title = "Сохранение"
|
||||
self.save_user_confirm.check_title(
|
||||
title,
|
||||
title,
|
||||
f"Confirmation dialog window with title '{title}' is missing"
|
||||
)
|
||||
self.save_user_confirm.click_allow_button()
|
||||
|
|
@ -196,27 +187,27 @@ class EditUserModalWindow(ModalWindowComponent):
|
|||
"""Инициирует сброс пароля пользователя."""
|
||||
reset_password_button = self.get_button_by_name("reset_password")
|
||||
reset_password_button.click()
|
||||
|
||||
|
||||
def check_content(self, user_name, role):
|
||||
"""Проверяет наличие и корректность всех элементов окна.
|
||||
|
||||
|
||||
Args:
|
||||
user_name (str): Ожидаемое имя пользователя
|
||||
role (str): Ожидаемая роль пользователя
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные
|
||||
"""
|
||||
self.check_by_window_title()
|
||||
self.check_toolbar_button_presence("close")
|
||||
self.check_toolbar_button_tooltip("close", "Закрыть")
|
||||
|
||||
|
||||
for name in self.content_items.keys():
|
||||
item = self.get_content_item(name)
|
||||
|
||||
|
||||
if name == "push_notification_checkbox_label":
|
||||
item.check_have_text(
|
||||
"Подписка на Push-уведомления",
|
||||
"Подписка на Push-уведомления",
|
||||
"Label 'Подписка на Push-уведомления' is missing"
|
||||
)
|
||||
elif name == "name_input":
|
||||
|
|
@ -236,8 +227,8 @@ class EditUserModalWindow(ModalWindowComponent):
|
|||
item.check_presence(
|
||||
f"Modal window content item with name '{name}' is missing"
|
||||
)
|
||||
|
||||
|
||||
self.check_button_presence("save")
|
||||
self.check_button_presence("delete")
|
||||
self.check_button_presence("reset_password")
|
||||
self.check_button_presence("close")
|
||||
self.check_button_presence("close")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"""Базовый класс страницы для работы с Playwright."""
|
||||
|
||||
from playwright.sync_api import Page, Response, APIRequestContext, expect
|
||||
from typing import Any, Dict, List, Optional
|
||||
from data.environment import host
|
||||
from tools.logger import get_logger
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional
|
||||
from playwright.sync_api import Page, Response, APIRequestContext, expect
|
||||
from tools.logger import get_logger
|
||||
from data.environment import host
|
||||
|
||||
logger = get_logger("BASE_PAGE")
|
||||
|
||||
|
|
@ -82,4 +82,4 @@ class BasePage:
|
|||
return False
|
||||
return True
|
||||
|
||||
assert compare_lists(actual, expected), msg
|
||||
assert compare_lists(actual, expected), msg
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
from pages.base_page import BasePage
|
||||
from components.alert_component import AlertComponent
|
||||
from elements.button_element import Button
|
||||
from components.json_container_component import JsonContainerComponent
|
||||
from elements.text_element import Text
|
||||
from elements.text_input_element import TextInput
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from locators.button_locators import ButtonLocators
|
||||
from locators.json_container_locators import JsonContainerLocators
|
||||
from locators.input_locators import InputLocators
|
||||
from locators.text_locators import TextLocators
|
||||
from playwright.sync_api import Page
|
||||
|
||||
from locators.text_locators import TextLocators
|
||||
from locators.input_locators import InputLocators
|
||||
from locators.json_container_locators import JsonContainerLocators
|
||||
from locators.button_locators import ButtonLocators
|
||||
from elements.text_input_element import TextInput
|
||||
from elements.text_element import Text
|
||||
from elements.button_element import Button
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.json_container_component import JsonContainerComponent
|
||||
from components.alert_component import AlertComponent
|
||||
from pages.base_page import BasePage
|
||||
|
||||
class LicenseTab(BasePage):
|
||||
"""Класс для работы с вкладкой 'Лицензии'.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page (Page): Экземпляр страницы Playwright.
|
||||
toolbar (ToolbarComponent): Компонент панели инструментов.
|
||||
|
|
@ -28,15 +27,15 @@ class LicenseTab(BasePage):
|
|||
|
||||
def __init__(self, page: Page) -> None:
|
||||
"""Инициализирует элементы вкладки 'Лицензии'.
|
||||
|
||||
|
||||
Args:
|
||||
page: Экземпляр страницы Playwright.
|
||||
"""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.toolbar = ToolbarComponent(page, "Лицензии")
|
||||
self.json_container = JsonContainerComponent(page)
|
||||
|
||||
|
||||
self.input_form_title = Text(page, TextLocators.TITLE_LICENSE_INPUT_FORM, "input form title")
|
||||
self.license_id = Text(page, TextLocators.LICENSE_ID, "license id")
|
||||
self.license_id_input = TextInput(page, InputLocators.LICENSE_ID_UPDATE, "license id input")
|
||||
|
|
@ -47,34 +46,34 @@ class LicenseTab(BasePage):
|
|||
# Действия:
|
||||
def fill_license_input_form(self, value: str) -> None:
|
||||
"""Заполняет форму ввода идентификатора лицензии и нажимает кнопку обновления.
|
||||
|
||||
|
||||
Args:
|
||||
value: Значение для ввода в поле идентификатора лицензии.
|
||||
"""
|
||||
self.license_id_input.clear()
|
||||
self.license_id_input.input_value(value)
|
||||
self.update_button.click()
|
||||
|
||||
|
||||
def scroll_json_container_up(self) -> None:
|
||||
"""Прокручивает JSON-контейнер вверх."""
|
||||
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first
|
||||
self.json_container.scroll_up(loc)
|
||||
|
||||
|
||||
def scroll_json_container_down(self) -> None:
|
||||
"""Прокручивает JSON-контейнер вниз."""
|
||||
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first
|
||||
self.json_container.scroll_down(loc)
|
||||
|
||||
# Проверки:
|
||||
|
||||
# Проверки:
|
||||
def check_json_container_verticall_scrolling(self) -> bool:
|
||||
"""Проверяет возможность вертикальной прокрутки JSON-контейнера.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True если контейнер можно прокручивать, иначе False.
|
||||
"""
|
||||
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first
|
||||
return self.json_container.is_scrollable_vertically(loc)
|
||||
|
||||
return self.json_container.is_scrollable_vertically(loc)
|
||||
|
||||
def check_content(self) -> None:
|
||||
"""Проверяет наличие всех основных элементов на вкладке."""
|
||||
self.should_be_toolbar()
|
||||
|
|
@ -82,50 +81,50 @@ class LicenseTab(BasePage):
|
|||
self.should_be_input_form_title()
|
||||
self.should_be_empty_input_form()
|
||||
self.should_be_update_button()
|
||||
|
||||
|
||||
def should_be_error_alert_window_with_text(self, text: str) -> None:
|
||||
"""Проверяет наличие и отсутствие алерта с указанным текстом.
|
||||
|
||||
|
||||
Args:
|
||||
text: Текст для проверки в алерте.
|
||||
"""
|
||||
self.error_alert.check_presence(text)
|
||||
self.error_alert.check_absence(text)
|
||||
self.error_alert.check_alert_presence(text)
|
||||
self.error_alert.check_alert_absence(text)
|
||||
|
||||
def should_be_toolbar(self) -> None:
|
||||
"""Проверяет наличие панели инструментов."""
|
||||
self.toolbar.check_presence("Toolbar is missing")
|
||||
|
||||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||||
|
||||
def should_be_json_container(self) -> None:
|
||||
"""Проверяет наличие JSON-контейнера с информацией о лицензии."""
|
||||
self.json_container.check_presence(
|
||||
JsonContainerLocators.CONTAINER,
|
||||
"Json container with license info is missing"
|
||||
)
|
||||
|
||||
|
||||
def should_be_input_form_title(self) -> None:
|
||||
"""Проверяет заголовок формы ввода и соответствие ID лицензии."""
|
||||
self.input_form_title.check_have_text(
|
||||
"Идентификатор:",
|
||||
"Input lisence id form title 'Идентификатор:' is missing"
|
||||
)
|
||||
|
||||
|
||||
actual_lisence_id = self.license_id.get_text(0).strip()
|
||||
|
||||
|
||||
# send request to backend to get license id
|
||||
response = self.send_get_api_request("e-cmdb/api/lic/deviceid")
|
||||
response_body = self.get_response_body(response)
|
||||
|
||||
|
||||
self.check_equals(
|
||||
actual_lisence_id,
|
||||
response_body['deviceId'],
|
||||
f"Expected ID value {response_body['deviceId']} is not equal actual value {actual_lisence_id}"
|
||||
)
|
||||
|
||||
|
||||
def should_be_empty_input_form(self) -> None:
|
||||
"""Проверяет, что форма ввода идентификатора лицензии пуста."""
|
||||
self.license_id_input.check_empty_input("Input lisence id form is missing or not empty")
|
||||
|
||||
|
||||
def should_be_update_button(self) -> None:
|
||||
"""Проверяет наличие кнопки обновления лицензии с правильным текстом."""
|
||||
button_text = "Обновить лицензию"
|
||||
|
|
@ -133,7 +132,7 @@ class LicenseTab(BasePage):
|
|||
button_text,
|
||||
f"Update button with text '{button_text}' is missing"
|
||||
)
|
||||
|
||||
|
||||
def verify_json_container_content(self) -> None:
|
||||
"""Проверяет соответствие содержимого JSON-контейнера данным из API."""
|
||||
actual_data = self.json_container.read_data(JsonContainerLocators.CONTAINER)
|
||||
|
|
@ -141,13 +140,13 @@ class LicenseTab(BasePage):
|
|||
# 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.json_container.check_json_equals(
|
||||
actual_data,
|
||||
response_body,
|
||||
"Expected json content is not equal actual:"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
from playwright.sync_api import Page
|
||||
|
||||
from elements.button_element import Button
|
||||
from elements.text_input_element import TextInput
|
||||
from elements.button_element import Button
|
||||
from data.environment import host
|
||||
from data.constants import Constants
|
||||
from components.alert_component import AlertComponent
|
||||
from pages.base_page import BasePage
|
||||
|
||||
from data.constants import Constants
|
||||
from data.environment import host
|
||||
|
||||
|
||||
class LoginPage(BasePage):
|
||||
"""Класс для работы со страницей авторизации.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page (Page): Экземпляр страницы Playwright.
|
||||
login_input (TextInput): Поле ввода логина.
|
||||
|
|
@ -22,28 +19,28 @@ class LoginPage(BasePage):
|
|||
|
||||
def __init__(self, page: Page) -> None:
|
||||
"""Инициализирует элементы страницы авторизации.
|
||||
|
||||
|
||||
Args:
|
||||
page: Экземпляр страницы Playwright.
|
||||
"""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.login_input = TextInput(page, page.get_by_label("Имя пользователя"), "login input")
|
||||
self.password_input = TextInput(page, page.get_by_label("Пароль"), "password input")
|
||||
self.login_button = Button(page, page.get_by_role("button"), "login button")
|
||||
|
||||
|
||||
self.error_alert = AlertComponent(page, "error")
|
||||
|
||||
|
||||
def do_login(self, username: str = None, password: str = None) -> None:
|
||||
"""Выполняет вход в систему.
|
||||
|
||||
|
||||
Если username/password не указаны, использует значения из Constants.
|
||||
Обрабатывает ответ сервера для получения токена доступа.
|
||||
|
||||
|
||||
Args:
|
||||
username: Логин пользователя. Если None, используется значение из Constants.
|
||||
password: Пароль пользователя. Если None, используется значение из Constants.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если после входа открылась неожиданная страница.
|
||||
"""
|
||||
|
|
@ -53,44 +50,44 @@ class LoginPage(BasePage):
|
|||
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.login_input.clear()
|
||||
self.login_input.input_value(actual_username)
|
||||
|
||||
|
||||
self.password_input.clear()
|
||||
self.password_input.input_value(actual_password)
|
||||
|
||||
|
||||
self.login_button.click()
|
||||
|
||||
|
||||
self.check_URL("dashboard", "An unexpected page has been opened")
|
||||
|
||||
|
||||
def do_unsuccessful_login(self, username: str = "someuser", password: str = "password") -> None:
|
||||
"""Выполняет попытку входа с неверными учетными данными.
|
||||
|
||||
|
||||
Можно передать свои неверные данные или использовать значения по умолчанию.
|
||||
Проверяет наличие сообщения об ошибке.
|
||||
|
||||
|
||||
Args:
|
||||
username: Неверный логин пользователя. По умолчанию "someuser".
|
||||
password: Неверный пароль пользователя. По умолчанию "password".
|
||||
"""
|
||||
self.open("")
|
||||
|
||||
|
||||
self.login_input.clear()
|
||||
self.login_input.input_value(username)
|
||||
|
||||
|
||||
self.password_input.clear()
|
||||
self.password_input.input_value(password)
|
||||
|
||||
|
||||
self.login_button.click()
|
||||
|
||||
self.error_alert.check_presence("Неверная пара логин/пароль")
|
||||
self.error_alert.check_absence("Неверная пара логин/пароль")
|
||||
|
||||
self.error_alert.check_alert_presence("Неверная пара логин/пароль")
|
||||
self.error_alert.check_alert_absence("Неверная пара логин/пароль")
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
from pages.base_page import BasePage
|
||||
from elements.button_element import Button
|
||||
from components.card_component import CardComponent
|
||||
from components.navbar_component import NavigationPanelComponent
|
||||
from playwright.sync_api import Page
|
||||
from locators.navigation_panel_locators import NavigationPanelLocators
|
||||
from locators.event_panel_locators import EventPanelLocators
|
||||
from playwright.sync_api import Page
|
||||
|
||||
from elements.button_element import Button
|
||||
from components.navbar_component import NavigationPanelComponent
|
||||
from components.card_component import CardComponent
|
||||
from pages.base_page import BasePage
|
||||
|
||||
class MainPage(BasePage):
|
||||
"""Класс для работы с главной страницей приложения.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
page (Page): Экземпляр страницы Playwright.
|
||||
navigation_panel (NavigationPanelComponent): Компонент панели навигации.
|
||||
|
|
@ -19,63 +18,63 @@ class MainPage(BasePage):
|
|||
|
||||
def __init__(self, page: Page) -> None:
|
||||
"""Инициализирует элементы главной страницы.
|
||||
|
||||
|
||||
Args:
|
||||
page: Экземпляр страницы Playwright.
|
||||
"""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.navigation_panel = NavigationPanelComponent(page)
|
||||
|
||||
|
||||
locators = self.page.locator(EventPanelLocators.BUTTONS_BLOCK).get_by_role("button").all()
|
||||
self.user_button = Button(page, locators[0], "search_button")
|
||||
self.user_button = Button(page, locators[1], "user_button")
|
||||
|
||||
|
||||
self.user_card = CardComponent(page)
|
||||
|
||||
# Действия:
|
||||
def click_main_navigation_panel_item(self, item_name: str) -> None:
|
||||
"""Кликает по элементу основной панели навигации.
|
||||
|
||||
|
||||
Args:
|
||||
item_name: Название элемента для клика.
|
||||
"""
|
||||
self.navigation_panel.click_item(NavigationPanelLocators.PANEL_MAIN, item_name)
|
||||
|
||||
|
||||
def click_configuration_navigation_panel_item(self, item_name: str) -> None:
|
||||
"""Кликает по элементу подраздела 'Конфигурация' в панели навигации.
|
||||
|
||||
|
||||
Args:
|
||||
item_name: Название элемента для клика.
|
||||
"""
|
||||
self.navigation_panel.click_sub_item(NavigationPanelLocators.PANEL_MAIN, 1, item_name)
|
||||
|
||||
|
||||
def click_maintenance_navigation_panel_item(self, item_name: str) -> None:
|
||||
"""Кликает по элементу подраздела 'Обслуживание' в панели навигации.
|
||||
|
||||
|
||||
Args:
|
||||
item_name: Название элемента для клика.
|
||||
"""
|
||||
self.navigation_panel.click_sub_item(NavigationPanelLocators.PANEL_MAIN, 2, item_name)
|
||||
|
||||
|
||||
def click_user_button(self) -> None:
|
||||
"""Кликает по кнопке пользователя."""
|
||||
self.user_button.click()
|
||||
|
||||
|
||||
def do_logout(self) -> None:
|
||||
"""Выполняет выход из системы."""
|
||||
self.should_be_user_button()
|
||||
self.click_user_button()
|
||||
self.user_card.click_logout_button()
|
||||
|
||||
|
||||
def scroll_navigation_panel_up(self) -> None:
|
||||
"""Прокручивает панель навигации вверх."""
|
||||
self.navigation_panel.scroll_up(NavigationPanelLocators.PANEL_SCROLL_CONTAINER)
|
||||
|
||||
|
||||
def scroll_navigation_panel_down(self) -> None:
|
||||
"""Прокручивает панель навигации вниз."""
|
||||
self.navigation_panel.scroll_down(NavigationPanelLocators.PANEL_SCROLL_CONTAINER)
|
||||
|
||||
|
||||
# Проверки:
|
||||
def should_be_navigation_panel(self) -> None:
|
||||
"""Проверяет наличие панели навигации."""
|
||||
|
|
@ -83,28 +82,28 @@ class MainPage(BasePage):
|
|||
NavigationPanelLocators.PANEL_MAIN,
|
||||
"Navigation panel is missing"
|
||||
)
|
||||
|
||||
|
||||
def should_be_user_button(self) -> None:
|
||||
"""Проверяет наличие кнопки пользователя."""
|
||||
self.user_button.check_presence("User button is missing on event panel")
|
||||
|
||||
|
||||
def check_navigation_panel_verticall_scrolling(self) -> bool:
|
||||
"""Проверяет возможность вертикальной прокрутки панели навигации.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True если панель можно прокручивать, иначе False.
|
||||
"""
|
||||
return self.navigation_panel.is_scrollable_vertically(
|
||||
NavigationPanelLocators.PANEL_SCROLL_CONTAINER
|
||||
)
|
||||
|
||||
|
||||
def check_navigation_panel_item_visibility(self, item_name: str) -> None:
|
||||
"""Проверяет видимость элемента в панели навигации.
|
||||
|
||||
|
||||
Args:
|
||||
item_name: Название элемента для проверки.
|
||||
"""
|
||||
self.navigation_panel.check_item_visibility(
|
||||
NavigationPanelLocators.PANEL_MAIN,
|
||||
item_name
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from pages.base_page import BasePage
|
||||
from playwright.sync_api import Page
|
||||
from locators.table_locators import TableLocators
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.table_component import TableComponent
|
||||
from locators.table_locators import TableLocators
|
||||
from playwright.sync_api import Page
|
||||
from pages.base_page import BasePage
|
||||
|
||||
|
||||
class ServiceStatusTab(BasePage):
|
||||
|
|
@ -17,126 +17,126 @@ class ServiceStatusTab(BasePage):
|
|||
def __init__(self, page: Page) -> None:
|
||||
"""Инициализация компонентов вкладки 'Статус обслуживания'."""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.toolbar = ToolbarComponent(page, "Статус обслуживания")
|
||||
self.services_table = TableComponent(page)
|
||||
|
||||
|
||||
def get_rows_count(self) -> int:
|
||||
"""Возвращает количество строк в таблице сервисов (без учёта заголовка).
|
||||
|
||||
Returns:
|
||||
int: Количество строк с данными.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если таблица пуста.
|
||||
"""
|
||||
table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
rows_count = len(table_content)
|
||||
|
||||
|
||||
if rows_count == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
return rows_count - 1
|
||||
|
||||
|
||||
def scroll_services_table_up(self) -> None:
|
||||
"""Прокручивает таблицу сервисов вверх."""
|
||||
self.services_table.scroll_up(TableLocators.TABLE_SCROLL_CONTAINER)
|
||||
|
||||
|
||||
def scroll_services_table_down(self) -> None:
|
||||
"""Прокручивает таблицу сервисов вниз."""
|
||||
self.services_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER)
|
||||
|
||||
|
||||
def check_services_table_content(self) -> None:
|
||||
"""Проверяет содержимое таблицы сервисов.
|
||||
|
||||
|
||||
Проверяет:
|
||||
- Наличие заголовков таблицы
|
||||
- Соответствие заголовков ожидаемым значениям
|
||||
- Наличие хотя бы одной строки с данными
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если таблица пуста или заголовки не соответствуют ожидаемым.
|
||||
"""
|
||||
expected_headers = [
|
||||
'Контейнер',
|
||||
'Время создания',
|
||||
'Статус',
|
||||
'Время работы',
|
||||
'Image ID',
|
||||
'Контейнер',
|
||||
'Время создания',
|
||||
'Статус',
|
||||
'Время работы',
|
||||
'Image ID',
|
||||
'Image ТЭГ'
|
||||
]
|
||||
|
||||
|
||||
table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
|
||||
|
||||
if len(table_content) == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
actual_headers = table_content[0]
|
||||
|
||||
|
||||
self.check_equals(
|
||||
actual_headers,
|
||||
actual_headers,
|
||||
expected_headers,
|
||||
f"Expected table headers {expected_headers} are not equal {actual_headers}"
|
||||
)
|
||||
|
||||
|
||||
if len(table_content) == 1:
|
||||
assert False, "Table body is missing"
|
||||
|
||||
|
||||
def check_services_table_verticall_scrolling(self) -> bool:
|
||||
"""Проверяет возможность вертикальной прокрутки таблицы.
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True если прокрутка возможна, иначе False.
|
||||
"""
|
||||
return self.services_table.is_scrollable_vertically(
|
||||
TableLocators.TABLE_SCROLL_CONTAINER
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
def check_services_table_first_row_visibility(self) -> None:
|
||||
"""Проверяет видимость первой строки таблицы.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если первая строка не видна.
|
||||
"""
|
||||
self.services_table.check_first_row_visibility(TableLocators.TABLE_WORK_AREA)
|
||||
|
||||
|
||||
def check_services_table_last_row_visibility(self) -> None:
|
||||
"""Проверяет видимость последней строки таблицы.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если последняя строка не видна.
|
||||
"""
|
||||
self.services_table.check_last_row_visibility(TableLocators.TABLE_WORK_AREA)
|
||||
|
||||
|
||||
def check_services_table_row_highlighting(self, row_index: int) -> None:
|
||||
"""Проверяет выделение указанной строки таблицы.
|
||||
|
||||
|
||||
Args:
|
||||
row_index (int): Индекс проверяемой строки.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если строка не выделена.
|
||||
"""
|
||||
self.services_table.check_row_highlighting(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
row_index
|
||||
)
|
||||
|
||||
|
||||
def should_be_toolbar(self) -> None:
|
||||
"""Проверяет наличие тулбара на вкладке.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если тулбар отсутствует.
|
||||
"""
|
||||
self.toolbar.check_presence("Toolbar is missing")
|
||||
|
||||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||||
|
||||
def should_be_services_table(self) -> None:
|
||||
"""Проверяет наличие таблицы сервисов.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если таблица отсутствует.
|
||||
"""
|
||||
self.services_table.check_presence(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
"Service statuses table is missing"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from pages.base_page import BasePage
|
||||
from playwright.sync_api import Page, Locator
|
||||
from locators.table_locators import TableLocators
|
||||
from locators.button_locators import ButtonLocators
|
||||
from elements.tooltip_button_element import TooltipButton
|
||||
from data.roles_dict import roles_dict
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.table_component import TableComponent
|
||||
from locators.button_locators import ButtonLocators
|
||||
from locators.table_locators import TableLocators
|
||||
from playwright.sync_api import Page, Locator
|
||||
from data.roles_dict import roles_dict
|
||||
|
||||
from pages.base_page import BasePage
|
||||
|
||||
class SessionsTab(BasePage):
|
||||
"""Класс для работы с вкладкой 'Сессия'.
|
||||
|
|
@ -20,7 +19,7 @@ class SessionsTab(BasePage):
|
|||
def __init__(self, page: Page) -> None:
|
||||
"""Инициализация компонентов вкладки 'Сессия'."""
|
||||
super().__init__(page)
|
||||
|
||||
|
||||
self.toolbar = ToolbarComponent(page, "Сессия")
|
||||
self.sessions_table = TableComponent(page)
|
||||
|
||||
|
|
@ -29,16 +28,16 @@ class SessionsTab(BasePage):
|
|||
|
||||
Returns:
|
||||
int: Количество строк с данными.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если таблица пуста.
|
||||
"""
|
||||
table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
rows_count = len(table_content)
|
||||
|
||||
|
||||
if rows_count == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
return rows_count - 1
|
||||
|
||||
def get_delete_session_button_from_row(self, row_index: int) -> TooltipButton:
|
||||
|
|
@ -54,11 +53,11 @@ class SessionsTab(BasePage):
|
|||
AssertionError: Если строка не найдена.
|
||||
"""
|
||||
row_locator = self.sessions_table.get_row_locator(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
row_index
|
||||
)
|
||||
assert isinstance(row_locator, Locator), f"Row with index {row_index} is missing"
|
||||
|
||||
|
||||
button_locator = row_locator.locator(ButtonLocators.BUTTON_DELETE_SESSION)
|
||||
return TooltipButton(self.page, button_locator, "delete_session_button")
|
||||
|
||||
|
|
@ -80,33 +79,33 @@ class SessionsTab(BasePage):
|
|||
AssertionError: Если таблица пуста или заголовки не соответствуют.
|
||||
"""
|
||||
expected_headers = [
|
||||
'ID сессии',
|
||||
'ID пользователя',
|
||||
'Время жизни',
|
||||
'Роль',
|
||||
'ID сессии',
|
||||
'ID пользователя',
|
||||
'Время жизни',
|
||||
'Роль',
|
||||
'Адрес'
|
||||
]
|
||||
|
||||
|
||||
table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
len_table_content = len(table_content)
|
||||
|
||||
|
||||
if len_table_content == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
actual_headers = table_content[0]
|
||||
|
||||
|
||||
self.check_equals(
|
||||
actual_headers,
|
||||
expected_headers,
|
||||
f"Expected table headers {expected_headers} are not equal {actual_headers}"
|
||||
)
|
||||
|
||||
|
||||
if len_table_content == 1:
|
||||
assert False, "Table body is missing"
|
||||
|
||||
|
||||
if verify:
|
||||
self.verify_sessions_table_content(table_content)
|
||||
|
||||
|
||||
for index in range(len_table_content - 1):
|
||||
self.should_be_delete_button_on_sessions_table_row(index, "Удалить")
|
||||
|
||||
|
|
@ -146,7 +145,7 @@ class SessionsTab(BasePage):
|
|||
AssertionError: Если строка не выделена.
|
||||
"""
|
||||
self.sessions_table.check_row_highlighting(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
row_index
|
||||
)
|
||||
|
||||
|
|
@ -156,7 +155,7 @@ class SessionsTab(BasePage):
|
|||
Raises:
|
||||
AssertionError: Если тулбар отсутствует.
|
||||
"""
|
||||
self.toolbar.check_presence("Toolbar is missing")
|
||||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||||
|
||||
def should_be_sessions_table(self) -> None:
|
||||
"""Проверяет наличие таблицы сессий.
|
||||
|
|
@ -165,13 +164,13 @@ class SessionsTab(BasePage):
|
|||
AssertionError: Если таблица отсутствует.
|
||||
"""
|
||||
self.sessions_table.check_presence(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
"Sessions table is missing"
|
||||
)
|
||||
|
||||
def should_be_delete_button_on_sessions_table_row(
|
||||
self,
|
||||
row_index: int,
|
||||
self,
|
||||
row_index: int,
|
||||
tooltip: str
|
||||
) -> None:
|
||||
"""Проверяет наличие кнопки удаления в строке таблицы.
|
||||
|
|
@ -199,19 +198,19 @@ class SessionsTab(BasePage):
|
|||
AssertionError: Если данные не соответствуют.
|
||||
"""
|
||||
expected_sessions_list = []
|
||||
|
||||
|
||||
# Отправка запроса к бэкенду для получения информации о сессиях
|
||||
response = self.send_get_api_request("e-nms/auth/sessions")
|
||||
response_body = self.get_response_body(response)
|
||||
|
||||
|
||||
for item in response_body:
|
||||
session_info = []
|
||||
session_info.append(item["id"])
|
||||
session_info.append(item["userId"])
|
||||
|
||||
|
||||
# Временно неподдерживаемое поле: время жизни сессии
|
||||
session_info.append("")
|
||||
|
||||
|
||||
roles = []
|
||||
for role in item["roles"]:
|
||||
if role in roles_dict.keys():
|
||||
|
|
@ -219,13 +218,13 @@ class SessionsTab(BasePage):
|
|||
|
||||
session_info.append(",".join(roles))
|
||||
session_info.append(item["ip"])
|
||||
|
||||
|
||||
expected_sessions_list.append(session_info)
|
||||
|
||||
del sessions_table[0] # Удаляем заголовок
|
||||
|
||||
|
||||
self.check_lists_equals(
|
||||
sessions_table,
|
||||
expected_sessions_list,
|
||||
"Actual sessions list is not equal expected users list on base db"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
from pages.base_page import BasePage
|
||||
from components.alert_component import AlertComponent
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.table_component import TableComponent
|
||||
from modal_windows.modal_add_user import AddUserModalWindow
|
||||
import re
|
||||
from playwright.sync_api import Page
|
||||
from modal_windows.modal_edit_user import EditUserModalWindow
|
||||
from modal_windows.modal_add_user import AddUserModalWindow
|
||||
from locators.table_locators import TableLocators
|
||||
from data.roles_dict import roles_dict
|
||||
from playwright.sync_api import Page
|
||||
import re
|
||||
|
||||
from components.toolbar_component import ToolbarComponent
|
||||
from components.table_component import TableComponent
|
||||
from components.alert_component import AlertComponent
|
||||
from pages.base_page import BasePage
|
||||
|
||||
class UsersTab(BasePage):
|
||||
"""Класс для работы с вкладкой 'Пользователи'.
|
||||
|
|
@ -30,12 +29,12 @@ class UsersTab(BasePage):
|
|||
locator_button_2 = self.page.get_by_role("navigation").filter(
|
||||
has_text=re.compile("Пользователи")
|
||||
).get_by_role("button").nth(1)
|
||||
|
||||
|
||||
self.toolbar = ToolbarComponent(page, "Пользователи")
|
||||
self.toolbar.add_button(locator_button_1, "edit")
|
||||
self.toolbar.add_button(locator_button_1, "add_user")
|
||||
self.toolbar.add_button(locator_button_2, "close")
|
||||
|
||||
|
||||
self.users_table = TableComponent(page)
|
||||
self.modal_windows = {}
|
||||
self.success_alert = AlertComponent(page, "success")
|
||||
|
|
@ -51,9 +50,9 @@ class UsersTab(BasePage):
|
|||
AssertionError: Если указан неподдерживаемый тип окна.
|
||||
"""
|
||||
if window_type == "add_user":
|
||||
self.modal_windows["add_user"] = AddUserModalWindow(self.page)
|
||||
self.modal_windows["add_user"] = AddUserModalWindow(self.page)
|
||||
elif window_type == "edit_user":
|
||||
self.modal_windows[title] = EditUserModalWindow(self.page, title)
|
||||
self.modal_windows[title] = EditUserModalWindow(self.page, title)
|
||||
else:
|
||||
assert False, "Unsupported modal window type"
|
||||
|
||||
|
|
@ -141,8 +140,8 @@ class UsersTab(BasePage):
|
|||
AssertionError: Если не отображается сообщение об успешном добавлении.
|
||||
"""
|
||||
self.get_modal_window("add_user").new_user(user_data)
|
||||
self.success_alert.check_presence(' Новый пользователь \n успешно добавлен! ')
|
||||
self.success_alert.check_absence(' Новый пользователь \n успешно добавлен! ')
|
||||
self.success_alert.check_alert_presence(' Новый пользователь \n успешно добавлен! ')
|
||||
self.success_alert.check_alert_absence(' Новый пользователь \n успешно добавлен! ')
|
||||
|
||||
def delete_user(self, user_name: str) -> None:
|
||||
"""Удаляет пользователя.
|
||||
|
|
@ -154,8 +153,8 @@ class UsersTab(BasePage):
|
|||
AssertionError: Если не отображается сообщение об успешном удалении.
|
||||
"""
|
||||
self.get_modal_window(user_name).delete_user()
|
||||
self.success_alert.check_presence('\nПользователь удалён\n')
|
||||
self.success_alert.check_absence('\nПользователь удалён\n')
|
||||
self.success_alert.check_alert_presence('\nПользователь удалён\n')
|
||||
self.success_alert.check_alert_absence('\nПользователь удалён\n')
|
||||
|
||||
def edit_user(self, user_name: str, user_data: dict) -> None:
|
||||
"""Редактирует данные пользователя.
|
||||
|
|
@ -168,8 +167,8 @@ class UsersTab(BasePage):
|
|||
AssertionError: Если не отображается сообщение об успешном обновлении.
|
||||
"""
|
||||
self.get_modal_window(user_name).edit_user(user_data)
|
||||
self.success_alert.check_presence('\nОбновление успешно\n')
|
||||
self.success_alert.check_absence('\nОбновление успешно\n')
|
||||
self.success_alert.check_alert_presence('\nОбновление успешно\n')
|
||||
self.success_alert.check_alert_absence('\nОбновление успешно\n')
|
||||
|
||||
def reset_password(self, user_name: str) -> str:
|
||||
"""Сбрасывает пароль пользователя.
|
||||
|
|
@ -182,12 +181,12 @@ class UsersTab(BasePage):
|
|||
"""
|
||||
new_password = ""
|
||||
self.get_modal_window(user_name).reset_password()
|
||||
|
||||
self.success_alert.check_presence("")
|
||||
|
||||
self.success_alert.check_alert_presence("")
|
||||
alert_message = self.success_alert.get_text()
|
||||
if len(alert_message) > 0:
|
||||
new_password = re.findall(r'[\d]+', alert_message)[0]
|
||||
|
||||
|
||||
return new_password
|
||||
|
||||
def find_user_in_table(self, name: str, role: str) -> int:
|
||||
|
|
@ -206,9 +205,9 @@ class UsersTab(BasePage):
|
|||
table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
if len(table_content) == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
del table_content[0] # Удаляем заголовок
|
||||
|
||||
|
||||
for row_index, user_info in enumerate(table_content):
|
||||
if name in user_info and role in user_info:
|
||||
return row_index
|
||||
|
|
@ -216,14 +215,14 @@ class UsersTab(BasePage):
|
|||
|
||||
def open_add_user_window(self) -> None:
|
||||
"""Открывает окно добавления пользователя.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если кнопки недоступны или окно не открылось.
|
||||
"""
|
||||
if self.toolbar.is_button_not_present("close"):
|
||||
self.toolbar.check_button_presence("edit")
|
||||
self.toolbar.click_button("edit")
|
||||
|
||||
|
||||
self.toolbar.check_button_presence("add_user")
|
||||
self.toolbar.click_button("add_user")
|
||||
self.add_modal_window("add_user", "")
|
||||
|
|
@ -243,26 +242,26 @@ class UsersTab(BasePage):
|
|||
"""
|
||||
tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"}
|
||||
table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
|
||||
|
||||
if len(table_content) == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
del table_content[0] # Удаляем заголовок
|
||||
|
||||
|
||||
if row_index >= len(table_content):
|
||||
assert False, "Row_index is out of range"
|
||||
|
||||
|
||||
user_name = table_content[row_index][0]
|
||||
for key, val in tmp_dict.items():
|
||||
if user_name == val:
|
||||
user_name = key
|
||||
|
||||
role = table_content[row_index][1]
|
||||
|
||||
user_name = key
|
||||
|
||||
role = table_content[row_index][2]
|
||||
|
||||
self.page.locator(TableLocators.TABLE_WORK_AREA).locator("//tbody/tr").nth(row_index).click()
|
||||
self.add_modal_window("edit_user", user_name)
|
||||
self.get_modal_window(user_name).check_by_window_title()
|
||||
|
||||
|
||||
return user_name, role
|
||||
|
||||
def open_edit_user_page_by_user(self, user_name: str, role: str) -> None:
|
||||
|
|
@ -275,10 +274,10 @@ class UsersTab(BasePage):
|
|||
Raises:
|
||||
AssertionError: Если пользователь не найден.
|
||||
"""
|
||||
row_index = self.find_user_in_table(user_name, role)
|
||||
row_index = self.find_user_in_table(user_name, role)
|
||||
if row_index == -1:
|
||||
assert False, f"User with name {user_name} and role {role} has not been found"
|
||||
|
||||
|
||||
self.page.locator(TableLocators.TABLE_WORK_AREA).locator("//tbody/tr").nth(row_index).click()
|
||||
self.add_modal_window("edit_user", user_name)
|
||||
self.get_modal_window(user_name).check_by_window_title()
|
||||
|
|
@ -292,22 +291,22 @@ class UsersTab(BasePage):
|
|||
Raises:
|
||||
AssertionError: Если таблица пуста или заголовки не соответствуют.
|
||||
"""
|
||||
expected_headers = ['Имя пользователя', 'Роль', 'E-mail', 'Номер для СМС']
|
||||
expected_headers = ['Имя пользователя', 'Тип авторизации', 'Роль', 'E-mail', 'Номер для СМС']
|
||||
table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
|
||||
|
||||
|
||||
if len(table_content) == 0:
|
||||
assert False, "The contents of the table are missing"
|
||||
|
||||
|
||||
actual_headers = table_content[0]
|
||||
self.check_equals(
|
||||
actual_headers,
|
||||
actual_headers,
|
||||
expected_headers,
|
||||
f"Expected table headers {expected_headers} are not equal {actual_headers}"
|
||||
)
|
||||
|
||||
|
||||
if len(table_content) == 1:
|
||||
assert False, "Table body is missing"
|
||||
|
||||
|
||||
if verify:
|
||||
self.verify_users_table_content(table_content)
|
||||
|
||||
|
|
@ -322,44 +321,44 @@ class UsersTab(BasePage):
|
|||
user_name (str): Имя пользователя
|
||||
role (str): Роль пользователя
|
||||
"""
|
||||
edit_user_window = self.get_modal_window(user_name)
|
||||
edit_user_window = self.get_modal_window(user_name)
|
||||
edit_user_window.check_content(user_name, role)
|
||||
|
||||
def should_be_toolbar(self) -> None:
|
||||
"""Проверяет наличие тулбара.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если тулбар или кнопка редактирования отсутствуют.
|
||||
"""
|
||||
self.toolbar.check_presence("Toolbar is missing")
|
||||
self.toolbar.check_toolbar_presence("Toolbar is missing")
|
||||
self.toolbar.check_button_presence("edit")
|
||||
|
||||
def should_be_toolbar_buttons(self) -> None:
|
||||
"""Проверяет наличие и функциональность кнопок тулбара.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если кнопки недоступны или имеют некорректные подсказки.
|
||||
"""
|
||||
self.toolbar.check_button_presence("edit")
|
||||
self.toolbar.check_button_presence("edit")
|
||||
self.toolbar.check_button_tooltip("edit", "Редактировать")
|
||||
|
||||
|
||||
self.toolbar.get_button_by_name("edit").click()
|
||||
self.toolbar.check_button_presence("add_user")
|
||||
self.toolbar.check_button_presence("close")
|
||||
self.toolbar.check_button_presence("close")
|
||||
self.toolbar.check_button_tooltip("add_user", "Добавить")
|
||||
self.toolbar.check_button_tooltip("close", "Закрыть")
|
||||
|
||||
|
||||
self.toolbar.get_button_by_name("close").click()
|
||||
self.toolbar.check_button_presence("edit")
|
||||
|
||||
def should_be_users_table(self) -> None:
|
||||
"""Проверяет наличие таблицы пользователей.
|
||||
|
||||
|
||||
Raises:
|
||||
AssertionError: Если таблица отсутствует.
|
||||
"""
|
||||
self.users_table.check_presence(
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
TableLocators.TABLE_WORK_AREA,
|
||||
"Users table is missing"
|
||||
)
|
||||
|
||||
|
|
@ -373,7 +372,7 @@ class UsersTab(BasePage):
|
|||
Raises:
|
||||
AssertionError: Если пользователь не найден.
|
||||
"""
|
||||
found = self.find_user_in_table(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"
|
||||
|
||||
|
|
@ -387,7 +386,7 @@ class UsersTab(BasePage):
|
|||
Raises:
|
||||
AssertionError: Если пользователь найден.
|
||||
"""
|
||||
found = self.find_user_in_table(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"
|
||||
|
||||
|
|
@ -402,26 +401,31 @@ class UsersTab(BasePage):
|
|||
"""
|
||||
expected_users_list = []
|
||||
tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"}
|
||||
|
||||
|
||||
query = {
|
||||
"id": ["/catalogs/user"],
|
||||
"id": ["/catalogs/user"],
|
||||
"data": {
|
||||
"namePath": True,
|
||||
"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 = []
|
||||
user_name = item["name"]
|
||||
|
||||
|
||||
if user_name in tmp_dict.keys():
|
||||
item["name"] = tmp_dict[user_name]
|
||||
user_info.append(item["name"])
|
||||
|
||||
|
||||
if item["type_auth"] is not None:
|
||||
user_info.append(item["type_auth"])
|
||||
else:
|
||||
user_info.append("")
|
||||
|
||||
if item["role"] is not None:
|
||||
role = item["role"]
|
||||
if role in roles_dict.keys():
|
||||
|
|
@ -429,23 +433,23 @@ class UsersTab(BasePage):
|
|||
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)
|
||||
|
||||
|
||||
del users_table[0] # Удаляем заголовок
|
||||
|
||||
|
||||
self.check_lists_equals(
|
||||
users_table,
|
||||
users_table,
|
||||
expected_users_list,
|
||||
"Actual users list is not equal expected users list on base db"
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.license_tab import LicenseTab
|
||||
from playwright.sync_api import Page
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
from pages.license_tab import LicenseTab
|
||||
|
||||
|
||||
class TestJsonContainer:
|
||||
|
|
@ -13,7 +13,7 @@ class TestJsonContainer:
|
|||
"""Фикстура для настройки тестового окружения."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.should_be_navigation_panel()
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -26,9 +26,9 @@ class TestJsonContainer:
|
|||
|
||||
is_scrollable = lt.check_json_container_verticall_scrolling()
|
||||
assert is_scrollable, "Should be verticall scrolling"
|
||||
|
||||
|
||||
lt.scroll_json_container_down()
|
||||
lt.wait_for_timeout(3000)
|
||||
|
||||
|
||||
lt.scroll_json_container_up()
|
||||
lt.wait_for_timeout(2000)
|
||||
lt.wait_for_timeout(2000)
|
||||
|
|
|
|||
|
|
@ -1,58 +1,57 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
# Запуск с viewport: {'width': 300, 'height': 420}
|
||||
|
||||
# @pytest.mark.smoke
|
||||
class TestNavigationPanel:
|
||||
"""Класс тестов для проверки панели навигации.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
browser: фикстура для работы с браузером
|
||||
"""
|
||||
|
||||
|
||||
def test_verticall_scrolling(self, browser):
|
||||
"""Тест вертикальной прокрутки панели навигации.
|
||||
|
||||
|
||||
Аргументы:
|
||||
browser: фикстура для работы с браузером
|
||||
|
||||
|
||||
Возвращает:
|
||||
None
|
||||
|
||||
|
||||
Исключения:
|
||||
AssertionError: если панель навигации не поддерживает вертикальную прокрутку
|
||||
"""
|
||||
# Действия:
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
# Мы на главной странице
|
||||
mp = MainPage(browser)
|
||||
|
||||
|
||||
# Проверки:
|
||||
# Проверяем наличие панели навигации
|
||||
mp.should_be_navigation_panel()
|
||||
|
||||
|
||||
# Открываем все пункты панели
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
mp.click_configuration_navigation_panel_item("Аутентификация")
|
||||
mp.click_configuration_navigation_panel_item("Уведомления")
|
||||
mp.click_configuration_navigation_panel_item("Аутентификация")
|
||||
mp.click_configuration_navigation_panel_item("Уведомления")
|
||||
mp.click_configuration_navigation_panel_item("Обслуживание и диагностика")
|
||||
mp.click_configuration_navigation_panel_item("Zero Touch Provisioning")
|
||||
|
||||
|
||||
# Проверяем возможность вертикальной прокрутки
|
||||
is_scrollable = mp.check_navigation_panel_verticall_scrolling()
|
||||
assert is_scrollable, "Should be vertical scrolling"
|
||||
|
||||
|
||||
# Действия:
|
||||
# Прокручиваем вверх и проверяем видимость элемента
|
||||
mp.scroll_navigation_panel_up()
|
||||
mp.check_navigation_panel_item_visibility("Панель приборов")
|
||||
mp.wait_for_timeout(3000)
|
||||
|
||||
# Прокручиваем вниз и проверяем видимость элемента
|
||||
|
||||
# Прокручиваем вниз и проверяем видимость элемента Настройки/ZTP/Шаблоны
|
||||
mp.scroll_navigation_panel_down()
|
||||
mp.check_navigation_panel_item_visibility("Шаблоны")
|
||||
mp.wait_for_timeout(2000)
|
||||
mp.check_navigation_panel_item_visibility("Шаблоны_2")
|
||||
mp.wait_for_timeout(2000)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.service_status_tab import ServiceStatusTab
|
||||
from playwright.sync_api import Page
|
||||
import pytest
|
||||
|
||||
from playwright.sync_api import Page
|
||||
from pages.service_status_tab import ServiceStatusTab
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestServiceStatusTable:
|
||||
"""Тесты для проверки таблицы статусов сервисов."""
|
||||
|
|
@ -13,7 +12,7 @@ class TestServiceStatusTable:
|
|||
"""Фикстура для настройки тестового окружения."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.should_be_navigation_panel()
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -23,17 +22,17 @@ class TestServiceStatusTable:
|
|||
def test_scrolling(self, browser: Page) -> None:
|
||||
"""Тест проверки прокрутки таблицы статусов сервисов."""
|
||||
sst = ServiceStatusTab(browser)
|
||||
|
||||
|
||||
sst.should_be_services_table()
|
||||
sst.check_services_table_content()
|
||||
|
||||
|
||||
is_scrollable_vertically = sst.check_services_table_verticall_scrolling()
|
||||
assert is_scrollable_vertically, "Should be vertical scrolling"
|
||||
|
||||
|
||||
sst.scroll_services_table_down()
|
||||
sst.check_services_table_last_row_visibility()
|
||||
sst.wait_for_timeout(3000)
|
||||
|
||||
|
||||
sst.scroll_services_table_up()
|
||||
sst.check_services_table_first_row_visibility()
|
||||
sst.wait_for_timeout(2000)
|
||||
sst.wait_for_timeout(2000)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.users_tab import UsersTab
|
||||
from playwright.sync_api import Page
|
||||
import pytest
|
||||
|
||||
from playwright.sync_api import Page
|
||||
from pages.users_tab import UsersTab
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestUsersModalWindow:
|
||||
"""Тесты для проверки модальных окон работы с пользователями."""
|
||||
|
|
@ -13,7 +12,7 @@ class TestUsersModalWindow:
|
|||
"""Фикстура для настройки тестового окружения."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.should_be_navigation_panel()
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -25,21 +24,21 @@ class TestUsersModalWindow:
|
|||
ut = UsersTab(browser)
|
||||
user_name, role = ut.open_edit_user_page_by_index(0)
|
||||
modal_window = ut.get_modal_window(user_name)
|
||||
|
||||
|
||||
is_scrollable_vertically = modal_window.check_window_vertical_scrolling()
|
||||
assert is_scrollable_vertically, "Should be vertical scrolling"
|
||||
|
||||
|
||||
modal_window.scroll_window_down()
|
||||
modal_window.check_button_presence("close")
|
||||
ut.wait_for_timeout(3000)
|
||||
|
||||
|
||||
modal_window.scroll_window_up()
|
||||
modal_window.check_toolbar_button_presence("close")
|
||||
ut.wait_for_timeout(3000)
|
||||
|
||||
|
||||
is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling()
|
||||
assert is_scrollable_horizontally, "Should be horizontal scrolling"
|
||||
|
||||
|
||||
modal_window.scroll_window_right()
|
||||
ut.wait_for_timeout(3000)
|
||||
modal_window.scroll_window_left()
|
||||
|
|
@ -50,22 +49,23 @@ class TestUsersModalWindow:
|
|||
ut = UsersTab(browser)
|
||||
ut.open_add_user_window()
|
||||
modal_window = ut.get_modal_window("add_user")
|
||||
|
||||
|
||||
is_scrollable_vertically = modal_window.check_window_vertical_scrolling()
|
||||
assert is_scrollable_vertically, "Should be vertical scrolling"
|
||||
|
||||
|
||||
modal_window.scroll_window_down()
|
||||
modal_window.check_button_presence("close")
|
||||
ut.wait_for_timeout(3000)
|
||||
|
||||
|
||||
modal_window.scroll_window_up()
|
||||
modal_window.check_toolbar_button_presence("close")
|
||||
ut.wait_for_timeout(3000)
|
||||
|
||||
is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling()
|
||||
assert is_scrollable_horizontally, "Should be horizontal scrolling"
|
||||
|
||||
modal_window.scroll_window_right()
|
||||
ut.wait_for_timeout(3000)
|
||||
modal_window.scroll_window_left()
|
||||
ut.wait_for_timeout(2000)
|
||||
|
||||
## Временно закомментарено - для окна добавления пользователя убрали горизонтальный скроллинг - BUG???
|
||||
# is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling()
|
||||
# assert is_scrollable_horizontally, "Should be horizontal scrolling"
|
||||
|
||||
# modal_window.scroll_window_right()
|
||||
# ut.wait_for_timeout(3000)
|
||||
# modal_window.scroll_window_left()
|
||||
# ut.wait_for_timeout(2000)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.license_tab import LicenseTab
|
||||
from playwright.sync_api import Page
|
||||
import pytest
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
from pages.license_tab import LicenseTab
|
||||
|
||||
class TestLicenseTab:
|
||||
"""Тесты для вкладки 'Лицензии'."""
|
||||
|
|
@ -15,7 +14,7 @@ class TestLicenseTab:
|
|||
"""Подготовка тестового окружения."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.should_be_navigation_panel()
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -26,7 +25,7 @@ class TestLicenseTab:
|
|||
"""Тест содержимого вкладки 'Лицензии'."""
|
||||
lt = LicenseTab(browser)
|
||||
lt.check_content()
|
||||
|
||||
|
||||
def test_license_tab_input_form_and_check_alert(self, browser: Page) -> None:
|
||||
"""Тест формы ввода лицензии и проверки алертов."""
|
||||
def gen_test_data() -> List[str]:
|
||||
|
|
@ -34,24 +33,24 @@ class TestLicenseTab:
|
|||
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(lowercase_str + "fffffffff")
|
||||
data.append("0")
|
||||
data.append("000000000000000000000000000000000000000000000000")
|
||||
data.append("-1")
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
lt = LicenseTab(browser)
|
||||
lt.should_be_empty_input_form()
|
||||
|
||||
|
||||
lt.fill_license_input_form("")
|
||||
lt.should_be_error_alert_window_with_text("Неверный лицензионный ключ")
|
||||
|
||||
|
||||
data = gen_test_data()
|
||||
|
||||
|
||||
for data_string in data:
|
||||
lt.fill_license_input_form(data_string)
|
||||
lt.should_be_error_alert_window_with_text("Ошибка обновления лицензии")
|
||||
lt.fill_license_input_form(data_string)
|
||||
lt.should_be_error_alert_window_with_text("Ошибка обновления лицензии")
|
||||
|
|
|
|||
|
|
@ -1,26 +1,24 @@
|
|||
import pytest
|
||||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from playwright.sync_api import Page
|
||||
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestLogin:
|
||||
"""Тесты для функционала входа и выхода из системы."""
|
||||
|
||||
|
||||
def test_successful_login(self, browser: Page) -> None:
|
||||
"""Тест успешного входа в систему."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
def test_unsuccessful_login(self, browser: Page) -> None:
|
||||
"""Тест неудачного входа в систему."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_unsuccessful_login()
|
||||
|
||||
|
||||
def test_successful_login_and_logout(self, browser: Page) -> None:
|
||||
"""Тест успешного входа и выхода из системы."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.do_logout()
|
||||
mp.do_logout()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.service_status_tab import ServiceStatusTab
|
||||
import pytest
|
||||
|
||||
from pages.service_status_tab import ServiceStatusTab
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestServiceStatusTab:
|
||||
"""Набор тестов для вкладки 'Статус обслуживания'.
|
||||
|
|
@ -20,16 +19,16 @@ class TestServiceStatusTab:
|
|||
"""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
# Переход на главную страницу
|
||||
mp = MainPage(browser)
|
||||
|
||||
|
||||
# Проверка наличия панели навигации
|
||||
mp.should_be_navigation_panel()
|
||||
|
||||
|
||||
# Клик по пункту 'Настройки' в главной панели навигации
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
||||
|
||||
# Клик по пункту 'Обслуживание и диагностика' в панели навигации настроек
|
||||
mp.click_configuration_navigation_panel_item("Обслуживание и диагностика")
|
||||
|
||||
|
|
@ -48,13 +47,13 @@ class TestServiceStatusTab:
|
|||
|
||||
# Проверка тулбара вкладки
|
||||
sst.should_be_toolbar()
|
||||
|
||||
|
||||
# Проверка наличия таблицы статусов сервисов
|
||||
sst.should_be_services_table()
|
||||
|
||||
|
||||
# Проверка содержимого таблицы сервисов
|
||||
sst.check_services_table_content()
|
||||
|
||||
|
||||
def test_service_status_table_row_highlighting(self, browser):
|
||||
"""Тест выделения строк в таблице сервисов.
|
||||
|
||||
|
|
@ -67,14 +66,14 @@ class TestServiceStatusTab:
|
|||
|
||||
# Проверка тулбара вкладки
|
||||
sst.should_be_toolbar()
|
||||
|
||||
|
||||
# Проверка наличия таблицы статусов сервисов
|
||||
sst.should_be_services_table()
|
||||
|
||||
|
||||
# Получение количества строк в таблице
|
||||
rows_count = sst.get_rows_count()
|
||||
|
||||
|
||||
# Проверка выделения строк
|
||||
sst.check_services_table_row_highlighting(0)
|
||||
sst.check_services_table_row_highlighting(rows_count - 1)
|
||||
sst.check_services_table_row_highlighting(int(rows_count / 2))
|
||||
sst.check_services_table_row_highlighting(int(rows_count / 2))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.session_tab import SessionsTab
|
||||
import pytest
|
||||
|
||||
from pages.session_tab import SessionsTab
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestSessionsTab:
|
||||
"""Набор тестов для вкладки 'Сеансы'.
|
||||
|
|
@ -21,10 +20,10 @@ class TestSessionsTab:
|
|||
# Авторизация в системе
|
||||
login_page = LoginPage(browser)
|
||||
login_page.do_login()
|
||||
|
||||
|
||||
# Инициализация главной страницы
|
||||
main_page = MainPage(browser)
|
||||
|
||||
|
||||
# Проверка и взаимодействие с элементами навигации
|
||||
main_page.should_be_navigation_panel()
|
||||
main_page.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -45,6 +44,6 @@ class TestSessionsTab:
|
|||
# Проверка элементов интерфейса
|
||||
sessions_tab.should_be_toolbar()
|
||||
sessions_tab.should_be_sessions_table()
|
||||
|
||||
|
||||
# Проверка содержимого таблицы с верификацией данных из БД
|
||||
sessions_tab.check_sessions_table_content(verify=True)
|
||||
sessions_tab.check_sessions_table_content(verify=True)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from pages.login_page import LoginPage
|
||||
from pages.main_page import MainPage
|
||||
from pages.users_tab import UsersTab
|
||||
from playwright.sync_api import Page
|
||||
import pytest
|
||||
from typing import Dict
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
from pages.users_tab import UsersTab
|
||||
from pages.main_page import MainPage
|
||||
from pages.login_page import LoginPage
|
||||
|
||||
class TestUsersTab:
|
||||
"""Тесты для вкладки 'Пользователи'."""
|
||||
|
|
@ -14,7 +13,7 @@ class TestUsersTab:
|
|||
"""Подготовка тестового окружения."""
|
||||
lp = LoginPage(browser)
|
||||
lp.do_login()
|
||||
|
||||
|
||||
mp = MainPage(browser)
|
||||
mp.should_be_navigation_panel()
|
||||
mp.click_main_navigation_panel_item("Настройки")
|
||||
|
|
@ -26,7 +25,7 @@ class TestUsersTab:
|
|||
ut.should_be_toolbar()
|
||||
ut.should_be_users_table()
|
||||
ut.check_users_table_content(True)
|
||||
|
||||
|
||||
def test_users_tab_toolbar_buttons(self, browser: Page) -> None:
|
||||
"""Тест кнопок на панели инструментов."""
|
||||
ut = UsersTab(browser)
|
||||
|
|
@ -37,12 +36,13 @@ class TestUsersTab:
|
|||
ut = UsersTab(browser)
|
||||
ut.open_add_user_window()
|
||||
ut.check_add_user_window_content()
|
||||
|
||||
|
||||
def test_add_user_window_close_buttons(self, browser: Page) -> None:
|
||||
"""Тест кнопок закрытия окна добавления пользователя."""
|
||||
ut = UsersTab(browser)
|
||||
ut.open_add_user_window()
|
||||
ut.open_add_user_window()
|
||||
ut.close_add_user_window_by_toolbar_button()
|
||||
|
||||
ut.open_add_user_window()
|
||||
ut.close_add_user_window()
|
||||
|
||||
|
|
@ -55,18 +55,18 @@ class TestUsersTab:
|
|||
def test_edit_user_window_close_buttons(self, browser: Page) -> None:
|
||||
"""Тест кнопок закрытия окна редактирования пользователя."""
|
||||
ut = UsersTab(browser)
|
||||
user_name, role = ut.open_edit_user_page_by_index(0)
|
||||
user_name, role = ut.open_edit_user_page_by_index(0)
|
||||
ut.close_edit_user_window_by_toolbar_button(user_name)
|
||||
user_name, role = ut.open_edit_user_page_by_index(0)
|
||||
user_name, role = ut.open_edit_user_page_by_index(0)
|
||||
ut.close_edit_user_window(user_name)
|
||||
|
||||
|
||||
def test_add_and_delete_user(self, browser: Page) -> None:
|
||||
"""Тест добавления и удаления пользователя."""
|
||||
user_data: Dict[str, str] = {"name": "User", "role": "Администратор"}
|
||||
|
||||
user_data: Dict[str, str] = {"name": "User", "role": "Администратор", "password": "987654"}
|
||||
|
||||
mp = MainPage(browser)
|
||||
ut = UsersTab(browser)
|
||||
|
||||
|
||||
ut.open_add_user_window()
|
||||
ut.add_new_user(user_data)
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
|
|
@ -80,21 +80,21 @@ class TestUsersTab:
|
|||
|
||||
def test_reset_password(self, browser: Page) -> None:
|
||||
"""Тест сброса пароля пользователя."""
|
||||
user_data: Dict[str, str] = {"name": "autoadmin", "role": "Администратор"}
|
||||
|
||||
user_data: Dict[str, str] = {"name": "autoadmin", "role": "Администратор", "password": "123456"}
|
||||
|
||||
mp = MainPage(browser)
|
||||
ut = UsersTab(browser)
|
||||
|
||||
|
||||
ut.open_add_user_window()
|
||||
ut.add_new_user(user_data)
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
ut.open_edit_user_page_by_user(user_data["name"], user_data["role"])
|
||||
new_password = ut.reset_password(user_data["name"])
|
||||
|
||||
if len(new_password) == 0:
|
||||
|
||||
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)
|
||||
new_mp = MainPage(browser)
|
||||
|
|
@ -111,14 +111,14 @@ class TestUsersTab:
|
|||
mp_1.click_configuration_navigation_panel_item("Пользователи")
|
||||
mp_1.click_configuration_navigation_panel_item("Пользователи")
|
||||
ut_1.should_not_be_user_in_table(user_data["name"], user_data["role"])
|
||||
|
||||
|
||||
def test_edit_user_role(self, browser: Page) -> None:
|
||||
"""Тест изменения роли пользователя."""
|
||||
user_data: Dict[str, str] = {"name": "autooperator", "role": "Оператор"}
|
||||
|
||||
user_data: Dict[str, str] = {"name": "autooperator", "role": "Оператор", "password": "123245"}
|
||||
|
||||
mp = MainPage(browser)
|
||||
ut = UsersTab(browser)
|
||||
|
||||
|
||||
ut.open_add_user_window()
|
||||
ut.add_new_user(user_data)
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
|
|
@ -133,4 +133,4 @@ class TestUsersTab:
|
|||
ut.delete_user(user_data["name"])
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
mp.click_configuration_navigation_panel_item("Пользователи")
|
||||
ut.should_not_be_user_in_table(user_data["name"], new_user_data["role"])
|
||||
ut.should_not_be_user_in_table(user_data["name"], new_user_data["role"])
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ INIT_TEMPLATE: str = """# Auto-generated by fix_python_project.py
|
|||
|
||||
class ProjectFixer:
|
||||
"""Основной класс для исправления структуры Python-проекта.
|
||||
|
||||
|
||||
Атрибуты:
|
||||
root_dir (str): Корневая директория проекта.
|
||||
log (List[str]): Список записей лога выполненных операций.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, root_dir: str = '.'):
|
||||
"""Инициализирует экземпляр ProjectFixer.
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ class ProjectFixer:
|
|||
|
||||
def remove_bom(self, filepath: str) -> bool:
|
||||
"""Удаляет BOM-маркер из файла, если он присутствует.
|
||||
|
||||
|
||||
Обрабатывает все файлы, включая находящиеся в tests/.
|
||||
|
||||
Args:
|
||||
|
|
@ -57,7 +57,7 @@ class ProjectFixer:
|
|||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
|
||||
if content.startswith(b'\xEF\xBB\xBF'):
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(content[3:])
|
||||
|
|
@ -69,7 +69,7 @@ class ProjectFixer:
|
|||
|
||||
def should_skip_init(self, dir_path: str) -> bool:
|
||||
"""Проверяет, нужно ли пропустить создание __init__.py в директории.
|
||||
|
||||
|
||||
Игнорирует служебные папки (tests/, .git/ и др.).
|
||||
|
||||
Args:
|
||||
|
|
@ -79,7 +79,7 @@ class ProjectFixer:
|
|||
bool: True, если директорию следует пропустить.
|
||||
"""
|
||||
dir_name = os.path.basename(dir_path)
|
||||
return (dir_name in INIT_IGNORED_DIRS or
|
||||
return (dir_name in INIT_IGNORED_DIRS or
|
||||
dir_name.startswith('.'))
|
||||
|
||||
def needs_init_py(self, dir_path: str) -> bool:
|
||||
|
|
@ -93,7 +93,7 @@ class ProjectFixer:
|
|||
"""
|
||||
if self.should_skip_init(dir_path):
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
items = os.listdir(dir_path)
|
||||
has_py_files = any(f.endswith('.py') and f != '__init__.py' for f in items)
|
||||
|
|
@ -149,14 +149,14 @@ class ProjectFixer:
|
|||
if __name__ == '__main__':
|
||||
# Обработка аргументов командной строки
|
||||
target_dir = sys.argv[1] if len(sys.argv) > 1 else '.'
|
||||
|
||||
|
||||
# Инициализация и запуск обработки
|
||||
fixer = ProjectFixer(target_dir)
|
||||
print(f"Исправление структуры проекта в: {fixer.root_dir}")
|
||||
|
||||
|
||||
fixer.process_directory()
|
||||
fixer.save_log()
|
||||
|
||||
|
||||
# Вывод результатов
|
||||
print(f"Готово! Внесено {len(fixer.log)} изменений.")
|
||||
print(f"Подробности сохранены в project_fix.log")
|
||||
print("Подробности сохранены в project_fix.log")
|
||||
|
|
|
|||
|
|
@ -34,4 +34,4 @@ def get_logger(name: str) -> logging.Logger:
|
|||
|
||||
logger.addHandler(handler)
|
||||
|
||||
return logger
|
||||
return logger
|
||||
|
|
|
|||
Loading…
Reference in New Issue