Правка code style, актуализация тестов под текущие изменения GUI

pull/1/head
nsubbot 2025-07-23 08:20:36 +03:00
parent ce6fe0390c
commit 10315ba38f
52 changed files with 899 additions and 782 deletions

View File

@ -7,4 +7,9 @@ tooltiplocator
- components\toolbar_component.py - добавлен tooltiplocator в сигнатуру функции check_button_tooltip, изменены функции add_button и get_button_by_name - components\toolbar_component.py - добавлен tooltiplocator в сигнатуру функции check_button_tooltip, изменены функции add_button и get_button_by_name
- pages\users_tab.py - переписана функция should_be_toolbar_buttons - pages\users_tab.py - переписана функция should_be_toolbar_buttons
- pages\session_tab.py - вкладка "Сессии" - pages\session_tab.py - вкладка "Сессии"
- tests\e2e\test_sessions_tab.py - тест вкладки "Сессии" - tests\e2e\test_sessions_tab.py - тест вкладки "Сессии"
===================23.07.2025==========================
- Все файлы прошли проверку pylint, внесены исправления для фикса замечаний линтера
- Возвращено заведение пользователя с введением пароля
- Актуализированы тесты под текущее состояние интерфейса пользователя версии 1.7

View File

@ -1,18 +1,16 @@
from playwright.sync_api import Page, expect 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 tools.logger import get_logger
from elements.text_element import Text
from components.base_component import BaseComponent
logger = get_logger("ALERT") logger = get_logger("ALERT")
class AlertComponent(BaseComponent): class AlertComponent(BaseComponent):
"""Компонент для работы с alert-окнами. """Компонент для работы с alert-окнами.
Поддерживает различные типы alert-окон: error, success, info, warning. Поддерживает различные типы alert-окон: error, success, info, warning.
Атрибуты: Атрибуты:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
alert_type: тип alert-окна (error/success/info/warning) alert_type: тип alert-окна (error/success/info/warning)
@ -21,69 +19,74 @@ class AlertComponent(BaseComponent):
def __init__(self, page: Page, alert_type: str): def __init__(self, page: Page, alert_type: str):
"""Инициализация компонента alert-окна. """Инициализация компонента alert-окна.
Args: Args:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
alert_type: тип alert-окна (error/success/info/warning) alert_type: тип alert-окна (error/success/info/warning)
Raises: Raises:
ValueError: если передан неподдерживаемый тип alert-окна ValueError: если передан неподдерживаемый тип alert-окна
""" """
super().__init__(page) super().__init__(page)
alert_types = ["error", "success", "info", "warning"] alert_types = ["error", "success", "info", "warning"]
if alert_type not in alert_types: if alert_type not in alert_types:
raise ValueError("Unsupported type of alert window") raise ValueError("Unsupported type of alert window")
self.alert_type = alert_type 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: def get_text(self) -> str:
"""Получение текста сообщения из alert-окна. """Получение текста сообщения из alert-окна.
Returns: Returns:
str: текст сообщения alert-окна str: текст сообщения alert-окна
""" """
return self.text.get_text(0) return self.text.get_text(0)
# Проверки: # Проверки:
def check_presence(self, text: str): def check_alert_presence(self, text: str):
"""Проверка наличия alert-окна с заданным текстом. """Проверка наличия alert-окна с заданным текстом.
Args: Args:
text: текст для проверки (если пустая строка - проверяется только наличие окна) text: текст для проверки (если пустая строка - проверяется только наличие окна)
Raises: Raises:
AssertionError: если alert-окно не найдено AssertionError: если alert-окно не найдено
""" """
msg = f"No {self.alert_type} alert window on page" msg = f"No {self.alert_type} alert window on page"
if text == "": if text == "":
expect(self.page.get_by_role("alert")).to_be_visible(), msg expect(self.page.get_by_role("alert")).to_be_visible(), msg
else: else:
expect(self.page.get_by_role("alert").filter(has_text=text)).to_be_visible(), msg 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-окна с заданным текстом. """Проверка отсутствия alert-окна с заданным текстом.
Args: Args:
text: текст для проверки text: текст для проверки
timeout: время ожидания исчезновения (в миллисекундах) timeout: время ожидания исчезновения (в миллисекундах)
Raises: Raises:
AssertionError: если alert-окно не исчезает в течение заданного времени AssertionError: если alert-окно не исчезает в течение заданного времени
""" """
seconds = int(timeout/1000) 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 expect(self.page.get_by_role("alert").filter(has_text=text)).to_be_hidden(timeout=timeout), msg
def check_text(self, alert_text: str): def check_text(self, alert_text: str):
"""Проверка точного соответствия текста в alert-окне. """Проверка точного соответствия текста в alert-окне.
Args: Args:
alert_text: ожидаемый текст сообщения alert_text: ожидаемый текст сообщения
Raises: Raises:
AssertionError: если текст не соответствует ожидаемому 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")

View File

@ -1,48 +1,171 @@
from playwright.sync_api import Page, Locator, expect from playwright.sync_api import Page, Locator, expect
from tools.logger import get_logger from tools.logger import get_logger
logger = get_logger("BASE_COMPONENT") logger = get_logger("BASE_COMPONENT")
class BaseComponent: class BaseComponent:
"""Базовый компонент для работы с элементами страницы.""" """Базовый компонент для работы с элементами страницы.
Предоставляет общие методы для взаимодействия с элементами:
- получение локаторов
- проверка видимости элементов
- работа с прокруткой
Атрибуты:
page: экземпляр страницы Playwright
"""
def __init__(self, page: Page): def __init__(self, page: Page):
"""Инициализация базового компонента.
Args:
page: экземпляр страницы Playwright
"""
self.page = page self.page = page
# Действия:
def get_locator(self, locator: str | Locator) -> Locator: def get_locator(self, locator: str | Locator) -> Locator:
"""Получение объекта Locator из строки или существующего Locator.
Args:
locator: строка с CSS/XPath селектором или объект Locator
Returns:
Locator: объект для работы с элементом
Raises:
TypeError: если передан некорректный тип локатора
"""
if isinstance(locator, Locator): if isinstance(locator, Locator):
return locator return locator
elif isinstance(locator, str): elif isinstance(locator, str):
return self.page.locator(locator) return self.page.locator(locator)
else: else:
raise TypeError("locator value should be string type or Locator type") 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) loc = self.get_locator(locator)
expect(loc).to_be_visible(visible=True, timeout=12000), msg 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) loc = self.get_locator(locator)
return loc.evaluate("el => el.scrollHeight > el.clientHeight") 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) loc = self.get_locator(locator)
return loc.evaluate("el => el.scrollWidth > el.clientWidth") 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 = self.get_locator(locator)
loc.evaluate("el => el.scrollTo(0, 0)") loc.evaluate("el => el.scrollTo(0, 0)")
loc.wait_for(timeout=2000)
def scroll_down(self, locator: str | Locator):
# Проверка позиции прокрутки
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 = self.get_locator(locator)
loc.evaluate("el => el.scrollTo(0, el.scrollHeight)") loc.evaluate("el => el.scrollTo(0, el.scrollHeight)")
loc.wait_for(timeout=2000)
def scroll_left(self, locator: str | Locator):
# Проверка позиции прокрутки
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) loc = self.get_locator(locator)
width = loc.evaluate("el => el.scrollWidth") width = loc.evaluate("el => el.scrollWidth")
loc.scroll_into_view_if_needed()
def scroll_right(self, locator: str | Locator): 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) 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"

View File

@ -1,18 +1,16 @@
from playwright.sync_api import Page 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 tools.logger import get_logger
from elements.button_element import Button
from components.base_component import BaseComponent
logger = get_logger("USER_CARD") logger = get_logger("USER_CARD")
class CardComponent(BaseComponent): class CardComponent(BaseComponent):
"""Компонент карточки пользователя. """Компонент карточки пользователя.
Предоставляет методы для взаимодействия с элементами карточки пользователя. Предоставляет методы для взаимодействия с элементами карточки пользователя.
Атрибуты: Атрибуты:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
logout_button: кнопка выхода из системы logout_button: кнопка выхода из системы
@ -20,25 +18,25 @@ class CardComponent(BaseComponent):
def __init__(self, page: Page): def __init__(self, page: Page):
"""Инициализация компонента карточки пользователя. """Инициализация компонента карточки пользователя.
Args: Args:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
""" """
super().__init__(page) super().__init__(page)
self.logout_button = Button( self.logout_button = Button(
page, page,
page.get_by_role("button", name="Выйти"), page.get_by_role("button", name="Выйти"),
"logout button" "logout button"
) )
# Действия: # Действия:
def click_logout_button(self): def click_logout_button(self):
"""Нажатие кнопки выхода из системы. """Нажатие кнопки выхода из системы.
Выполняет клик по кнопке 'Выйти' в карточке пользователя. Выполняет клик по кнопке 'Выйти' в карточке пользователя.
""" """
self.logout_button.click() self.logout_button.click()
# Проверки: # Проверки:
# (Методы проверок могут быть добавлены здесь в будущем) # (Методы проверок могут быть добавлены здесь в будущем)

View File

@ -1,54 +1,52 @@
from playwright.sync_api import Page 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 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") logger = get_logger("CONFIRM_WINDOW")
class ConfirmComponent(BaseComponent): class ConfirmComponent(BaseComponent):
"""Компонент окна подтверждения действий.""" """Компонент окна подтверждения действий."""
def __init__(self, page: Page, cancel_button_text: str, allow_button_text: str): def __init__(self, page: Page, cancel_button_text: str, allow_button_text: str):
super().__init__(page) super().__init__(page)
self.title = Text(page, ConfirmLocators.TITLE, "confirm title") self.title = Text(page, ConfirmLocators.TITLE, "confirm title")
self.text = Text(page, ConfirmLocators.TEXT, "confirm text") self.text = Text(page, ConfirmLocators.TEXT, "confirm text")
self.close_button = Button(page, ConfirmLocators.BUTTON_CLOSE, "confirm close button") self.close_button = Button(page, ConfirmLocators.BUTTON_CLOSE, "confirm close button")
self.cancel_button = Button( self.cancel_button = Button(
page, page,
page.get_by_role("button", name=cancel_button_text).first, page.get_by_role("button", name=cancel_button_text).first,
"confirm cancel button" "confirm cancel button"
) )
self.allow_button = Button( self.allow_button = Button(
page, page,
page.get_by_role("button", name=allow_button_text).first, page.get_by_role("button", name=allow_button_text).first,
"confirm allow button" "confirm allow button"
) )
# Действия: # Действия:
def click_allow_button(self) -> None: def click_allow_button(self) -> None:
"""Нажатие кнопки подтверждения действия.""" """Нажатие кнопки подтверждения действия."""
self.allow_button.click() self.allow_button.click()
def click_cancel_button(self) -> None: def click_cancel_button(self) -> None:
"""Нажатие кнопки отмены действия.""" """Нажатие кнопки отмены действия."""
self.cancel_button.click() self.cancel_button.click()
def click_close_button(self) -> None: def click_close_button(self) -> None:
"""Нажатие кнопки закрытия окна подтверждения.""" """Нажатие кнопки закрытия окна подтверждения."""
self.close_button.click() self.close_button.click()
# Проверки: # Проверки:
def check_title(self, title: str, msg: str) -> None: def check_title(self, title: str, msg: str) -> None:
"""Проверка текста заголовка окна подтверждения.""" """Проверка текста заголовка окна подтверждения."""
self.title.check_have_text(title, msg) self.title.check_have_text(title, msg)
def check_text(self, text: str, msg: str) -> None: def check_text(self, text: str, msg: str) -> None:
"""Проверка текста сообщения в окне подтверждения.""" """Проверка текста сообщения в окне подтверждения."""
self.text.check_have_text(text, msg) self.text.check_have_text(text, msg)

View File

@ -1,58 +1,57 @@
from playwright.sync_api import Page
import json import json
import jsondiff import jsondiff
from playwright.sync_api import Page
from components.base_component import BaseComponent
from tools.logger import get_logger from tools.logger import get_logger
from components.base_component import BaseComponent
logger = get_logger("JSON_CONTAINER") logger = get_logger("JSON_CONTAINER")
class JsonContainerComponent(BaseComponent): class JsonContainerComponent(BaseComponent):
"""Компонент для работы с JSON-данными на странице. """Компонент для работы с JSON-данными на странице.
Предоставляет методы для чтения и проверки JSON-данных, Предоставляет методы для чтения и проверки JSON-данных,
отображаемых в специальных контейнерах на странице. отображаемых в специальных контейнерах на странице.
Атрибуты: Атрибуты:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
""" """
def __init__(self, page: Page): def __init__(self, page: Page):
"""Инициализация JSON-контейнера. """Инициализация JSON-контейнера.
Args: Args:
page: экземпляр страницы Playwright page: экземпляр страницы Playwright
""" """
super().__init__(page) super().__init__(page)
# Действия: # Действия:
def read_data(self, locator): def read_data(self, locator):
"""Чтение и форматирование JSON-данных из указанного локатора. """Чтение и форматирование JSON-данных из указанного локатора.
Args: Args:
locator: локатор элемента, содержащего JSON-данные locator: локатор элемента, содержащего JSON-данные
Returns: Returns:
dict: распарсенный JSON-объект dict: распарсенный JSON-объект
Raises: Raises:
json.JSONDecodeError: если данные не могут быть преобразованы в JSON json.JSONDecodeError: если данные не могут быть преобразованы в JSON
""" """
def format_json_string(json_string): def format_json_string(json_string):
"""Вспомогательная функция для форматирования строки JSON. """Вспомогательная функция для форматирования строки JSON.
Args: Args:
json_string: сырая строка с JSON-данными json_string: сырая строка с JSON-данными
Returns: Returns:
str: отформатированная строка JSON str: отформатированная строка JSON
""" """
substrings = json_string.splitlines() substrings = json_string.splitlines()
formatted_string_list = [] formatted_string_list = []
last_substring = substrings.pop() last_substring = substrings.pop()
for substring in substrings: for substring in substrings:
if substring.find(':') == -1: if substring.find(':') == -1:
if substring == '{': if substring == '{':
formatted_string_list.append(substring) formatted_string_list.append(substring)
@ -63,38 +62,38 @@ class JsonContainerComponent(BaseComponent):
else: else:
formatted_string_list.append(substring + ',') formatted_string_list.append(substring + ',')
continue continue
key, value = substring.split(':') key, value = substring.split(':')
s = ':'.join(['"' + key + '" ', " " + value]) s = ':'.join(['"' + key + '" ', " " + value])
if value == '{': if value == '{':
formatted_string_list.append(s) formatted_string_list.append(s)
else: else:
formatted_string_list.append(s + ',') formatted_string_list.append(s + ',')
s2 = formatted_string_list.pop() s2 = formatted_string_list.pop()
formatted_string_list.append(s2.rstrip(',')) formatted_string_list.append(s2.rstrip(','))
formatted_string_list.append(last_substring) formatted_string_list.append(last_substring)
return " ".join(formatted_string_list) return " ".join(formatted_string_list)
# Чтение JSON-содержимого из рабочей области # Чтение JSON-содержимого из рабочей области
loc = self.get_locator(locator) loc = self.get_locator(locator)
json_string = loc.inner_text() json_string = loc.inner_text()
formatted_json_string = format_json_string(json_string) 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): def check_json_equals(self, actual, expected, msg):
"""Сравнение JSON-объектов на идентичность. """Сравнение JSON-объектов на идентичность.
Args: Args:
actual: фактический JSON-объект actual: фактический JSON-объект
expected: ожидаемый JSON-объект expected: ожидаемый JSON-объект
msg: сообщение об ошибке msg: сообщение об ошибке
Raises: Raises:
AssertionError: если объекты не идентичны AssertionError: если объекты не идентичны
""" """
diff = jsondiff.diff(expected, actual, syntax='symmetric') 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}"

View File

@ -1,11 +1,9 @@
from playwright.sync_api import Page 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 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") logger = get_logger("MODAL_WINDOW")
@ -23,81 +21,81 @@ class ModalWindowComponent(BaseComponent):
def add_content_item(self, name: str, item: object) -> None: def add_content_item(self, name: str, item: object) -> None:
"""Добавление элемента содержимого в окно.""" """Добавление элемента содержимого в окно."""
self.content_items[name] = item self.content_items[name] = item
def get_content_item(self, name: str) -> object | None: def get_content_item(self, name: str) -> object | None:
"""Получение элемента содержимого по имени.""" """Получение элемента содержимого по имени."""
return self.content_items.get(name) return self.content_items.get(name)
def add_toolbar_title(self, title: str) -> None: def add_toolbar_title(self, title: str) -> None:
"""Добавление заголовка в панель инструментов.""" """Добавление заголовка в панель инструментов."""
self.toolbar.add_title(title) self.toolbar.add_title(title)
def add_toolbar_button(self, locator: str, name: str) -> None: def add_toolbar_button(self, locator: str, name: str) -> None:
"""Добавление кнопки в панель инструментов.""" """Добавление кнопки в панель инструментов."""
self.toolbar.add_button(locator, name) self.toolbar.add_button(locator, name)
def add_button(self, locator: str, name: str) -> None: def add_button(self, locator: str, name: str) -> None:
"""Добавление кнопки в окно.""" """Добавление кнопки в окно."""
self.buttons.append(Button(self.page, locator, name)) self.buttons.append(Button(self.page, locator, name))
def get_button_by_name(self, name: str) -> Button | None: def get_button_by_name(self, name: str) -> Button | None:
"""Поиск кнопки по имени.""" """Поиск кнопки по имени."""
for button in self.buttons: for button in self.buttons:
if button.name == name: if button.name == name:
return button return button
return None return None
def click_button(self, name: str) -> None: def click_button(self, name: str) -> None:
"""Нажатие кнопки по имени.""" """Нажатие кнопки по имени."""
button = self.get_button_by_name(name) button = self.get_button_by_name(name)
if button is None: if button is None:
assert False, f"Button with name '{name}' not found" assert False, f"Button with name '{name}' not found"
button.click() button.click()
def click_toolbar_close_button(self) -> None: def click_toolbar_close_button(self) -> None:
"""Нажатие кнопки закрытия в панели инструментов.""" """Нажатие кнопки закрытия в панели инструментов."""
self.toolbar.click_button("close") self.toolbar.click_button("close")
def scroll_window_down(self) -> None: def scroll_window_down(self) -> None:
"""Прокрутка содержимого окна вниз.""" """Прокрутка содержимого окна вниз."""
self.scroll_down(ModalWindowLocators.MODAL_WINDOW) self.scroll_down(ModalWindowLocators.MODAL_WINDOW)
def scroll_window_up(self) -> None: def scroll_window_up(self) -> None:
"""Прокрутка содержимого окна вверх.""" """Прокрутка содержимого окна вверх."""
self.scroll_up(ModalWindowLocators.MODAL_WINDOW) self.scroll_up(ModalWindowLocators.MODAL_WINDOW)
def scroll_window_left(self) -> None: def scroll_window_left(self) -> None:
"""Прокрутка содержимого окна влево.""" """Прокрутка содержимого окна влево."""
self.scroll_left(ModalWindowLocators.MODAL_WINDOW) self.scroll_left(ModalWindowLocators.MODAL_WINDOW)
def scroll_window_right(self) -> None: def scroll_window_right(self) -> None:
"""Прокрутка содержимого окна вправо.""" """Прокрутка содержимого окна вправо."""
self.scroll_right(ModalWindowLocators.MODAL_WINDOW) self.scroll_right(ModalWindowLocators.MODAL_WINDOW)
# Проверки: # Проверки:
def check_window_vertical_scrolling(self) -> bool: def check_window_vertical_scrolling(self) -> bool:
"""Проверка возможности вертикальной прокрутки.""" """Проверка возможности вертикальной прокрутки."""
return self.is_scrollable_vertically(ModalWindowLocators.MODAL_WINDOW) return self.is_scrollable_vertically(ModalWindowLocators.MODAL_WINDOW)
def check_window_horizontal_scrolling(self) -> bool: def check_window_horizontal_scrolling(self) -> bool:
"""Проверка возможности горизонтальной прокрутки.""" """Проверка возможности горизонтальной прокрутки."""
return self.is_scrollable_horizontally(ModalWindowLocators.MODAL_WINDOW) return self.is_scrollable_horizontally(ModalWindowLocators.MODAL_WINDOW)
def check_by_window_title(self) -> None: 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: def check_button_presence(self, name: str) -> None:
"""Проверка наличия кнопки по имени.""" """Проверка наличия кнопки по имени."""
button = self.get_button_by_name(name) button = self.get_button_by_name(name)
if button is None: if button is None:
assert False, f"Button with name '{name}' not found" assert False, f"Button with name '{name}' not found"
button.check_presence(f"Button with name '{name}' is missing") button.check_presence(f"Button with name '{name}' is missing")
def check_toolbar_button_presence(self, name: str) -> None: def check_toolbar_button_presence(self, name: str) -> None:
"""Проверка наличия кнопки в панели инструментов.""" """Проверка наличия кнопки в панели инструментов."""
self.toolbar.check_button_presence(name) self.toolbar.check_button_presence(name)
def check_toolbar_button_tooltip(self, name: str, tooltip: str) -> None: 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)

View File

@ -1,9 +1,7 @@
from playwright.sync_api import Page, Locator 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 tools.logger import get_logger
from locators.navigation_panel_locators import NavigationPanelLocators
from components.base_component import BaseComponent
logger = get_logger("NAVIGATION_PANEL") logger = get_logger("NAVIGATION_PANEL")
@ -13,35 +11,46 @@ class NavigationPanelComponent(BaseComponent):
def __init__(self, page: Page): def __init__(self, page: Page):
super().__init__(page) super().__init__(page)
# Действия: # Действия:
def get_item_names(self, locator: str | Locator) -> list[str]: def get_item_names(self, locator: str | Locator) -> list[str]:
"""Получает тексты всех элементов по указанному локатору.""" """Получает тексты всех элементов по указанному локатору."""
loc = self.get_locator(locator) loc = self.get_locator(locator)
return loc.all_inner_texts() return loc.all_inner_texts()
def click_item(self, locator: str | Locator, item_name: str) -> None: def click_item(self, locator: str | Locator, item_name: str) -> None:
"""Кликает по элементу с указанным текстом.""" """Кликает по элементу с указанным текстом."""
loc = self.get_locator(locator) loc = self.get_locator(locator)
loc.get_by_text(item_name).click() loc.get_by_text(item_name).click()
def click_sub_item(self, locator: str | Locator, sublevel_number: int, item_name: str) -> None: def click_sub_item(self, locator: str | Locator, sublevel_number: int, item_name: str) -> None:
"""Кликает по вложенному элементу с указанным текстом.""" """Кликает по вложенному элементу с указанным текстом."""
root_locator = self.get_locator(NavigationPanelLocators.NODE_ROOT) root_locator = self.get_locator(NavigationPanelLocators.NODE_ROOT)
children_locator = self.get_locator(NavigationPanelLocators.NODE_CHILDREN) children_locator = self.get_locator(NavigationPanelLocators.NODE_CHILDREN)
loc = self.get_locator(locator) loc = self.get_locator(locator)
if sublevel_number == 1: if sublevel_number == 1:
loc.locator(root_locator).get_by_text(item_name).click() loc.locator(root_locator).get_by_text(item_name).click()
elif sublevel_number == 2: elif sublevel_number == 2:
loc.locator(children_locator).locator(root_locator).get_by_text(item_name).click() loc.locator(children_locator).locator(root_locator).get_by_text(item_name).click()
else: else:
raise ValueError("the navigation panel has two levels of nesting only") raise ValueError("the navigation panel has two levels of nesting only")
# Проверки: # Проверки:
def check_item_visibility(self, locator: str | Locator, item_name: str) -> None: 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" 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)

View File

@ -1,8 +1,6 @@
from playwright.sync_api import Page, expect, Locator from playwright.sync_api import Page, expect, Locator
from components.base_component import BaseComponent
from tools.logger import get_logger from tools.logger import get_logger
from components.base_component import BaseComponent
logger = get_logger("TABLE") logger = get_logger("TABLE")
@ -12,20 +10,31 @@ class TableComponent(BaseComponent):
def __init__(self, page: Page): def __init__(self, page: Page):
super().__init__(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]]: def read(self, locator: str | Locator) -> list[list[str]]:
"""Читает данные из таблицы.""" """Читает данные из таблицы."""
table_data = [] table_data = []
table = self.get_locator(locator) table = self.get_locator(locator)
# Чтение заголовка таблицы # Чтение заголовка таблицы
header_cells = table.locator("//thead/tr") header_cells = table.locator("//thead/tr")
header_cell_text = header_cells.nth(0).inner_text() header_cell_text = header_cells.nth(0).inner_text()
header_data = header_cell_text.split('\n') header_data = header_cell_text.split('\n')
table_data.append(header_data) table_data.append(header_data)
# Чтение ячеек таблицы # Чтение ячеек таблицы
rows = table.locator("//tbody/tr") rows = table.locator("//tbody/tr")
for i in range(rows.count()): for i in range(rows.count()):
@ -36,33 +45,33 @@ class TableComponent(BaseComponent):
cell_text = cells.nth(j).inner_text() cell_text = cells.nth(j).inner_text()
row_data.append(cell_text) row_data.append(cell_text)
table_data.append(row_data) table_data.append(row_data)
return table_data return table_data
# Проверки: # Проверки:
def check_first_row_visibility(self, locator: str | Locator) -> None: def check_first_row_visibility(self, locator: str | Locator) -> None:
"""Проверяет видимость первой строки таблицы.""" """Проверяет видимость первой строки таблицы."""
table = self.get_locator(locator) table = self.get_locator(locator)
first_row = table.locator("//tbody/tr").first first_row = table.locator("//tbody/tr").first
expect(first_row).to_be_visible(), "The first table row is not visible" expect(first_row).to_be_visible(), "The first table row is not visible"
def check_last_row_visibility(self, locator: str | Locator) -> None: def check_last_row_visibility(self, locator: str | Locator) -> None:
"""Проверяет видимость последней строки таблицы.""" """Проверяет видимость последней строки таблицы."""
table = self.get_locator(locator) table = self.get_locator(locator)
last_row = table.locator("//tbody/tr").last last_row = table.locator("//tbody/tr").last
expect(last_row).to_be_visible(), "The last table row is not visible" 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: def check_row_highlighting(self, locator: str | Locator, row_index: int) -> None:
"""Проверяет подсветку строки при наведении.""" """Проверяет подсветку строки при наведении."""
table = self.get_locator(locator) table = self.get_locator(locator)
row = table.locator("//tbody/tr").nth(row_index) row = table.locator("//tbody/tr").nth(row_index)
row.scroll_into_view_if_needed() row.scroll_into_view_if_needed()
hover_element = row.locator(".body-row-hover") hover_element = row.locator(".body-row-hover")
initial_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor") initial_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor")
row.hover() row.hover()
self.page.wait_for_timeout(300) self.page.wait_for_timeout(300)
new_color = hover_element.evaluate("el => window.getComputedStyle(el).backgroundColor") 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"

View File

@ -1,8 +1,8 @@
from playwright.sync_api import Page, expect, Locator 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 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") logger = get_logger("TOOLBAR")
@ -105,7 +105,7 @@ class ToolbarComponent(BaseComponent):
raise AssertionError(f"Unsupported button name {name}") raise AssertionError(f"Unsupported button name {name}")
return button.is_not_present(timeout=1000) # Ожидание 1 секунда return button.is_not_present(timeout=1000) # Ожидание 1 секунда
def check_presence(self, message: str) -> None: def check_toolbar_presence(self, message: str) -> None:
"""Проверяет видимость тулбара. """Проверяет видимость тулбара.
Args: Args:
@ -141,4 +141,4 @@ class ToolbarComponent(BaseComponent):
button = self.get_button_by_name(name) button = self.get_button_by_name(name)
if button is None: if button is None:
raise AssertionError(f"Unsupported button name {name}") raise AssertionError(f"Unsupported button name {name}")
button.check_tooltip_with_text(ToolbarLocators.TOOLTIP, tooltip) button.check_tooltip_with_text(ToolbarLocators.TOOLTIP, tooltip)

View File

@ -1,8 +1,7 @@
from dotenv import load_dotenv
import inspect
from pathlib import Path from pathlib import Path
import pytest
import os import os
import inspect
from dotenv import load_dotenv
load_dotenv() load_dotenv()
@ -14,17 +13,17 @@ def pytest_sessionfinish(session, exitstatus):
"""Генерация Markdown файлов с группировкой по классам""" """Генерация Markdown файлов с группировкой по классам"""
if session.config.getoption("--generate-md"): if session.config.getoption("--generate-md"):
tests_by_file = {} tests_by_file = {}
for item in session.items: for item in session.items:
if not (hasattr(item, 'function') and item.function.__doc__): if not (hasattr(item, 'function') and item.function.__doc__):
continue continue
file_path = str(item.fspath) file_path = str(item.fspath)
file_name = os.path.splitext(os.path.basename(file_path))[0] file_name = os.path.splitext(os.path.basename(file_path))[0]
if file_name not in tests_by_file: if file_name not in tests_by_file:
tests_by_file[file_name] = {} tests_by_file[file_name] = {}
# Группируем тесты по классам # Группируем тесты по классам
class_name = item.cls.__name__ if hasattr(item, 'cls') else "Без класса" class_name = item.cls.__name__ if hasattr(item, 'cls') else "Без класса"
if class_name not in tests_by_file[file_name]: if class_name not in tests_by_file[file_name]:
@ -33,26 +32,26 @@ def pytest_sessionfinish(session, exitstatus):
'tests': [] 'tests': []
} }
tests_by_file[file_name][class_name]['tests'].append(item) tests_by_file[file_name][class_name]['tests'].append(item)
# Создаем директорию docs # Создаем директорию docs
output_dir = Path("docs") output_dir = Path("docs")
output_dir.mkdir(exist_ok=True) output_dir.mkdir(exist_ok=True)
# Генерируем файлы # Генерируем файлы
for file_name, classes in tests_by_file.items(): for file_name, classes in tests_by_file.items():
md_content = f"# Документация тестов ({file_name}.py)\n\n" md_content = f"# Документация тестов ({file_name}.py)\n\n"
for class_name, class_data in classes.items(): for class_name, class_data in classes.items():
if class_name != "Без класса": if class_name != "Без класса":
md_content += f"## Класс: {class_name}\n" md_content += f"## Класс: {class_name}\n"
if class_data['doc']: if class_data['doc']:
md_content += f"{class_data['doc']}\n\n" md_content += f"{class_data['doc']}\n\n"
for item in class_data['tests']: for item in class_data['tests']:
md_content += f"### {item.name}\n" md_content += f"### {item.name}\n"
md_content += f"**Маркеры:** {', '.join(mark.name for mark in item.own_markers)}\n\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" md_content += f"```python\n{inspect.cleandoc(item.function.__doc__)}\n```\n\n"
output_file = output_dir / f"{file_name}.md" output_file = output_dir / f"{file_name}.md"
output_file.write_text(md_content, encoding='utf-8') output_file.write_text(md_content, encoding='utf-8')
@ -62,4 +61,4 @@ def pytest_addoption(parser):
action="store_true", action="store_true",
default=False, default=False,
help="Генерировать Markdown документацию с группировкой по классам" help="Генерировать Markdown документацию с группировкой по классам"
) )

View File

@ -1,6 +1,5 @@
import os import os
class Constants: class Constants:
"""Класс для хранения констант и переменных окружения. """Класс для хранения констант и переменных окружения.
@ -16,4 +15,4 @@ class Constants:
login = os.getenv('AUTH_LOGIN') login = os.getenv('AUTH_LOGIN')
password = os.getenv('AUTH_PASSWORD') password = os.getenv('AUTH_PASSWORD')
except KeyError: except KeyError:
print("LOGIN OR PASSWORD WASN'T FOUND") print("LOGIN OR PASSWORD WASN'T FOUND")

View File

@ -1,6 +1,5 @@
import os import os
from typing import Dict, Optional from typing import Dict
class Environment: class Environment:
"""Класс для работы с окружением и URL-адресами.""" """Класс для работы с окружением и URL-адресами."""
@ -29,20 +28,20 @@ class Environment:
return self.URLS[self.env] + "e-nms-ui/" return self.URLS[self.env] + "e-nms-ui/"
return self.URLS[self.env] return self.URLS[self.env]
raise Exception(f"Unknown value of ENV variable {self.env}") raise Exception(f"Unknown value of ENV variable {self.env}")
def get_request_url(self) -> str: def get_request_url(self) -> str:
"""Возвращает URL для API-запросов.""" """Возвращает URL для API-запросов."""
if self.env in self.URLS: if self.env in self.URLS:
return self.URLS[self.env] return self.URLS[self.env]
raise Exception(f"Unknown value of ENV variable {self.env}") raise Exception(f"Unknown value of ENV variable {self.env}")
def set_access_token(self, token: str) -> None: def set_access_token(self, token: str) -> None:
"""Устанавливает токен доступа.""" """Устанавливает токен доступа."""
self.token = token self.token = token
def get_access_token(self) -> str: def get_access_token(self) -> str:
"""Возвращает текущий токен доступа.""" """Возвращает текущий токен доступа."""
return self.token return self.token
host: Environment = Environment() host: Environment = Environment()

View File

@ -5,4 +5,4 @@ roles_dict = {
"operator": "Оператор", "operator": "Оператор",
"inform_secur_user": "Специалист информационной безопасности", "inform_secur_user": "Специалист информационной безопасности",
"user": "Пользователь" "user": "Пользователь"
} }

View File

@ -1,11 +1,8 @@
from playwright.sync_api import Page, Locator, expect, TimeoutError from playwright.sync_api import Page, Locator, expect, TimeoutError
from typing import Optional
from tools.logger import get_logger from tools.logger import get_logger
logger = get_logger("BASE_ELEMENT") logger = get_logger("BASE_ELEMENT")
class BaseElement: class BaseElement:
"""Базовый класс для работы с элементами страницы.""" """Базовый класс для работы с элементами страницы."""
@ -27,29 +24,29 @@ class BaseElement:
# Действия: # Действия:
def click(self) -> None: 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() self.locator.click()
def get_text(self, index: int) -> str: 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() return self.locator.nth(index).text_content()
def wait_for_element(self, timeout: int = 12000) -> None: 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) self.locator.wait_for(timeout=timeout)
# Проверки: # Проверки:
def check_have_text(self, text: str, msg: str) -> None: 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 expect(self.locator).to_have_text(text), msg
def check_presence(self, msg: str) -> None: 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) print(self.locator)
expect(self.locator).to_be_visible(visible=True, timeout=12000), msg expect(self.locator).to_be_visible(visible=True, timeout=12000), msg
def is_present(self, timeout: int = 5000) -> bool: 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: try:
self.locator.wait_for(timeout=timeout) self.locator.wait_for(timeout=timeout)
except TimeoutError: except TimeoutError:
@ -57,9 +54,9 @@ class BaseElement:
return True return True
def is_not_present(self, timeout: int = 5000) -> bool: 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: try:
self.locator.wait_for(timeout=timeout) self.locator.wait_for(timeout=timeout)
except TimeoutError: except TimeoutError:
return True return True
return False return False

View File

@ -1,7 +1,5 @@
from playwright.sync_api import expect
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("BUTTON") logger = get_logger("BUTTON")
@ -25,4 +23,4 @@ class Button(BaseElement):
# (Методы действий будут добавлены по мере необходимости) # (Методы действий будут добавлены по мере необходимости)
# Проверки: # Проверки:
# (Методы проверок будут добавлены по мере необходимости) # (Методы проверок будут добавлены по мере необходимости)

View File

@ -1,5 +1,5 @@
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("CHECKBOX") logger = get_logger("CHECKBOX")
@ -22,12 +22,12 @@ class Checkbox(BaseElement):
# Действия: # Действия:
def check(self) -> None: def check(self) -> None:
"""Устанавливает чекбокс в отмеченное состояние.""" """Устанавливает чекбокс в отмеченное состояние."""
logger.info(f'Checking checkbox "{self.name}"') logger.info(f"Checking checkbox '{self.name}'")
self.locator.check() self.locator.check()
def uncheck(self) -> None: def uncheck(self) -> None:
"""Снимает отметку с чекбокса.""" """Снимает отметку с чекбокса."""
logger.info(f'Unchecking checkbox "{self.name}"') logger.info(f"Unchecking checkbox '{self.name}'")
self.locator.uncheck() self.locator.uncheck()
# Проверки: # Проверки:
@ -37,5 +37,5 @@ class Checkbox(BaseElement):
Returns: Returns:
True, если чекбокс отмечен, иначе False. True, если чекбокс отмечен, иначе False.
""" """
logger.info(f'Checking if checkbox "{self.name}" is checked') logger.info(f"Checking if checkbox '{self.name}' is checked")
return self.locator.is_checked() return self.locator.is_checked()

View File

@ -1,10 +1,8 @@
from playwright.sync_api import expect
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("DROPDOWN_LIST") logger = get_logger("DROPDOWN_LIST")
class DropdownList(BaseElement): class DropdownList(BaseElement):
"""Класс для работы с выпадающими списками на странице. """Класс для работы с выпадающими списками на странице.
@ -43,4 +41,4 @@ class DropdownList(BaseElement):
logger.info(f'Checking item with text "{text}" in dropdown "{self.name}"') 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() enabled = self.page.get_by_role("listitem").filter(has_text=text).is_enabled()
if not 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"

View File

@ -1,9 +1,8 @@
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("TEXT") logger = get_logger("TEXT")
class Text(BaseElement): class Text(BaseElement):
"""Класс для работы с текстовыми элементами на странице. """Класс для работы с текстовыми элементами на странице.
@ -23,4 +22,4 @@ class Text(BaseElement):
# (Методы действий будут добавлены по мере необходимости) # (Методы действий будут добавлены по мере необходимости)
# Проверки: # Проверки:
# (Методы проверок будут добавлены по мере необходимости) # (Методы проверок будут добавлены по мере необходимости)

View File

@ -1,6 +1,6 @@
from playwright.sync_api import expect from playwright.sync_api import expect
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("TEXT_INPUT") logger = get_logger("TEXT_INPUT")
@ -56,4 +56,4 @@ class TextInput(BaseElement):
AssertionError: Если поле не пустое. AssertionError: Если поле не пустое.
""" """
logger.info(f'Checking that text input "{self.name}" is empty') 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

View File

@ -1,9 +1,8 @@
from elements.base_element import BaseElement
from tools.logger import get_logger from tools.logger import get_logger
from elements.base_element import BaseElement
logger = get_logger("TOOLTIP_BUTTON") logger = get_logger("TOOLTIP_BUTTON")
class TooltipButton(BaseElement): class TooltipButton(BaseElement):
"""Класс элемента кнопки с всплывающей подсказкой. """Класс элемента кнопки с всплывающей подсказкой.
@ -31,13 +30,13 @@ class TooltipButton(BaseElement):
""" """
# Наведение на элемент для отображения подсказки # Наведение на элемент для отображения подсказки
self.locator.hover() self.locator.hover()
# Получение элемента подсказки # Получение элемента подсказки
tooltip = self.page.locator(tooltip_locator) tooltip = self.page.locator(tooltip_locator)
# Проверка соответствия текста # Проверка соответствия текста
actual_text = tooltip.text_content().strip() actual_text = tooltip.text_content().strip()
assert actual_text == expected_text, ( assert actual_text == expected_text, (
f"Текст подсказки не соответствует ожидаемому. " f"Текст подсказки не соответствует ожидаемому. "
f"Ожидалось: '{expected_text}', получено: '{actual_text}'" f"Ожидалось: '{expected_text}', получено: '{actual_text}'"
) )

View File

@ -5,7 +5,6 @@
import pytest import pytest
from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright, Playwright from playwright.sync_api import Browser, BrowserContext, Page, sync_playwright, Playwright
import os
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
@ -24,20 +23,24 @@ def pytest_addoption(parser: Parser):
--t: Таймаут по умолчанию (мс) --t: Таймаут по умолчанию (мс)
--l: Локаль браузера --l: Локаль браузера
""" """
parser.addoption('--bn', action='store', default="chrome", parser.addoption('--bn', action='store', default="chrome",
help="Choose browser: chrome, remote_chrome or firefox") 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') 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') 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': 1080},
# parser.addoption('--s', action='store', default={'width': 1920, 'height': 300}, help='Size window: width,height') # help='Size window: width,height')
parser.addoption('--slow', action='store', default=200, # 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') help='Choose slow_mo for robot action')
parser.addoption('--t', action='store', default=60000, parser.addoption('--t', action='store', default=60000,
help='Choose timeout') help='Choose timeout')
parser.addoption('--l', action='store', default='ru-RU', parser.addoption('--l', action='store', default='ru-RU',
help='Choose locale') help='Choose locale')
# Закомментированная опция для Qase # Закомментированная опция для Qase
# parser.addini('qs_to_api_token', default=os.getenv("QASE_TOKEN"), help='Qase app token') # 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() playwright = sync_playwright().start()
# Выбор браузера на основе параметра командной строки # Выбор браузера на основе параметра командной строки
if request.config.getoption("bn") == 'remote_chrome': if request.config.getoption("bn") == 'remote_chrome':
browser = get_remote_chrome(playwright, request) browser = get_remote_chrome(playwright, request)
@ -78,9 +81,9 @@ def browser(request: FixtureRequest) -> Page:
browser = get_chrome_browser(playwright, request) browser = get_chrome_browser(playwright, request)
context = get_context(browser, request, 'local') context = get_context(browser, request, 'local')
page_data = context.new_page() page_data = context.new_page()
yield page_data yield page_data
# Очистка после завершения тестов # Очистка после завершения тестов
for context in browser.contexts: for context in browser.contexts:
context.close() context.close()
@ -177,8 +180,8 @@ def get_context(browser: Browser, request: FixtureRequest, start: str) -> Browse
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def return_back(browser: Page): def return_back(browser: Page):
"""Фикстура для возврата на предыдущую страницу в браузере. """Фикстура для возврата на предыдущую страницу в браузере.
Args: Args:
browser: Экземпляр страницы браузера. browser: Экземпляр страницы браузера.
""" """
browser.go_back() browser.go_back()

View File

@ -1,6 +1,4 @@
class ButtonLocators: class ButtonLocators:
BUTTON_LICENSE_UPDATE = "//div[@class='scrollarea__footer']//button" 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']"

View File

@ -10,4 +10,4 @@ class ConfirmLocators:
CONFIRM = "//div[contains(@class, 'v-dialog--active')]" CONFIRM = "//div[contains(@class, 'v-dialog--active')]"
TITLE = "//div[@class='v-card__title']/h3" TITLE = "//div[@class='v-card__title']/h3"
BUTTON_CLOSE = "//div[@class='vuedl-layout__closeBtn']" 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']"

View File

@ -5,4 +5,4 @@ class EventPanelLocators:
BUTTONS_BLOCK (str): XPath локатор блока кнопок в панели инструментов. BUTTONS_BLOCK (str): XPath локатор блока кнопок в панели инструментов.
Находится во втором блоке элементов toolbar'а внутри контентной области. Находится во втором блоке элементов 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]"

View File

@ -9,4 +9,4 @@ class InputLocators:
- Контейнер поля ввода (v-input__control) - Контейнер поля ввода (v-input__control)
- Непосредственно текстовое поле (textarea) - Непосредственно текстовое поле (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"

View File

@ -8,4 +8,4 @@ class JsonContainerLocators:
Ищет div с классом, содержащим 'scrollarea__body'. Ищет div с классом, содержащим 'scrollarea__body'.
""" """
CONTAINER = "//div[contains(@class,'jv-container')]" CONTAINER = "//div[contains(@class,'jv-container')]"
SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body')]" SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body')]"

View File

@ -4,17 +4,17 @@ class ModalWindowLocators:
Атрибуты: Атрибуты:
MODAL_WINDOW (str): XPath локатор активного модального окна. MODAL_WINDOW (str): XPath локатор активного модального окна.
INPUT_FORM_USER_DATA (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 локатор активного меню выбора ролей. ROLES_MENU_INPUT_FORM_USER_DATA (str): XPath локатор активного меню выбора ролей.
LABEL_INPUT_FORM_USER_DATA (str): XPath локатор метки поля ввода в форме. LABEL_INPUT_FORM_USER_DATA (str): XPath локатор метки поля ввода в форме.
""" """
MODAL_WINDOW = "//div[contains(@class, 'v-dialog--active')]" MODAL_WINDOW = "//div[contains(@class, 'v-dialog--active')]"
INPUT_FORM_USER_DATA = "//form[@class='v-form']" INPUT_FORM_USER_DATA = "//form[@class='v-form']"
TEXT_FIELD_INPUT_FORM_USER_DATA = "xpath=div[2]/div/div/div/div/input" 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_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')]" 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"

View File

@ -14,6 +14,6 @@ class NavigationPanelLocators:
""" """
PANEL_MAIN = "//ul[contains(@class, 'v-expansion-panel')]" PANEL_MAIN = "//ul[contains(@class, 'v-expansion-panel')]"
PANEL_SCROLL_CONTAINER = "//div[contains(@class, 'scrollarea__body') and .//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_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')]"

View File

@ -10,4 +10,4 @@ class TableLocators:
содержащего таблицу с классом scrolltable__container. содержащего таблицу с классом scrolltable__container.
""" """
TABLE_WORK_AREA = "//div[@class='scrollarea__body']/div/div/div/table" 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"

View File

@ -8,4 +8,4 @@ class TextLocators:
Ищет span с классами 'title' и 'text_select' (выделяемый текст). Ищет span с классами 'title' и 'text_select' (выделяемый текст).
""" """
TITLE_LICENSE_INPUT_FORM = "//span[@class='title']" TITLE_LICENSE_INPUT_FORM = "//span[@class='title']"
LICENSE_ID = "//span[@class='title text_select']" LICENSE_ID = "//span[@class='title text_select']"

View File

@ -12,4 +12,4 @@ class ToolbarLocators:
- 'menuable__content__active' (показанное состояние) - 'menuable__content__active' (показанное состояние)
""" """
TITLE = "//nav//div[contains(@class, 'v-toolbar__title')]" 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')]"

View File

@ -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 import re
from playwright.sync_api import Page
from tools.logger import get_logger 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") logger = get_logger("ADD_USER_MODAL_WINDOW")
@ -29,87 +30,87 @@ class AddUserModalWindow(ModalWindowComponent):
def __init__(self, page: Page): def __init__(self, page: Page):
"""Инициализация компонентов модального окна добавления пользователя.""" """Инициализация компонентов модального окна добавления пользователя."""
super().__init__(page) super().__init__(page)
# Локаторы элементов формы # Локаторы элементов формы
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA
roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA
# Настройка заголовка и кнопки закрытия тулбара # Настройка заголовка и кнопки закрытия тулбара
self.window_title = "Добавить нового пользователя" self.window_title = "Добавить нового пользователя"
locator_button_toolbar_close = self.page.get_by_role("navigation").filter( locator_button_toolbar_close = self.page.get_by_role("navigation").filter(
has_text=re.compile(self.window_title) has_text=re.compile(self.window_title)
).get_by_role("button") ).get_by_role("button")
self.add_toolbar_title(self.window_title) self.add_toolbar_title(self.window_title)
self.add_toolbar_button(locator_button_toolbar_close, "close") self.add_toolbar_button(locator_button_toolbar_close, "close")
# Добавление элементов формы # Добавление элементов формы
checkbox_1 = Checkbox( checkbox_1 = Checkbox(
page, page,
self.page.get_by_role("checkbox").nth(0), self.page.get_by_role("checkbox").nth(0),
"active_directory" "active_directory"
) )
self.add_content_item("active_directory_checkbox", checkbox_1) self.add_content_item("active_directory_checkbox", checkbox_1)
label_1 = Text( label_1 = Text(
page, page,
self.page.locator(label_locator).nth(0), self.page.locator(label_locator).nth(0),
"active_directory_checkbox_label" "active_directory_checkbox_label"
) )
self.add_content_item("active_directory_checkbox_label", label_1) 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) 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)
name_input = TextInput(page, loc, "name_input") name_input = TextInput(page, loc, "name_input")
self.add_content_item("name_input", 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") role_input = TextInput(page, role_loc, "role_input")
self.add_content_item("role_input", role_input) self.add_content_item("role_input", role_input)
self.add_content_item( self.add_content_item(
"roles_list", "roles_list",
DropdownList(page, roles_menu_locator, "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) loc = self.page.locator(input_form_locator).locator("xpath=div[5]").locator(text_field_locator)
commentary_input = TextInput(page, loc, "commentary_input") commentary_input = TextInput(page, loc, "commentary_input")
self.add_content_item("commentary_input", 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) loc = self.page.locator(input_form_locator).locator("xpath=div[6]").locator(text_field_locator)
email_input = TextInput(page, loc, "email_input") email_input = TextInput(page, loc, "email_input")
self.add_content_item("email_input", 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) loc = self.page.locator(input_form_locator).locator("xpath=div[7]").locator(text_field_locator)
phone_input = TextInput(page, loc, "phone_input") phone_input = TextInput(page, loc, "phone_input")
self.add_content_item("phone_input", phone_input) self.add_content_item("phone_input", phone_input)
checkbox_2 = Checkbox( checkbox_2 = Checkbox(
page, page,
page.get_by_role("checkbox").nth(1), page.get_by_role("checkbox").nth(1),
"push_notification" "push_notification"
) )
self.add_content_item("push_notification_checkbox", checkbox_2) self.add_content_item("push_notification_checkbox", checkbox_2)
label_2 = Text( label_2 = Text(
page, page,
self.page.locator(label_locator).nth(1), self.page.locator(label_locator).nth(1),
"push_notification_checkbox_label" "push_notification_checkbox_label"
) )
self.add_content_item("push_notification_checkbox_label", label_2) self.add_content_item("push_notification_checkbox_label", label_2)
# Добавление кнопок действий # Добавление кнопок действий
locator_button_add = self.page.get_by_role("button", name="Добавить") locator_button_add = self.page.get_by_role("button", name="Добавить")
self.add_button(locator_button_add, "add") self.add_button(locator_button_add, "add")
locator_button_close = self.page.get_by_role("button", name="Закрыть") locator_button_close = self.page.get_by_role("button", name="Закрыть")
self.add_button(locator_button_close, "close") self.add_button(locator_button_close, "close")
self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ") self.new_user_confirm = ConfirmComponent(page, " Отмена ", " Добавить ")
def new_user(self, user_data): def new_user(self, user_data):
@ -118,9 +119,9 @@ class AddUserModalWindow(ModalWindowComponent):
Args: Args:
user_data (dict): Словарь с данными пользователя. Может содержать ключи: user_data (dict): Словарь с данными пользователя. Может содержать ключи:
- active_directory_checked (bool): Состояние чекбокса Active Directory - active_directory_checked (bool): Состояние чекбокса Active Directory
- type_auth (str): Тип авторизации
- name (str): Имя пользователя - name (str): Имя пользователя
- role (str): Роль пользователя - role (str): Роль пользователя
- password (str): Пароль пользователя
- commentary (str): Комментарий - commentary (str): Комментарий
- email (str): Email - email (str): Email
- phone_number (str): Номер телефона - phone_number (str): Номер телефона
@ -130,57 +131,57 @@ class AddUserModalWindow(ModalWindowComponent):
AssertionError: Если подтверждающее окно не отображается AssertionError: Если подтверждающее окно не отображается
""" """
fields = user_data.keys() fields = user_data.keys()
if "active_directory_checked" in fields: if "active_directory_checked" in fields:
checkbox = self.get_content_item("active_directory_checkbox") checkbox = self.get_content_item("active_directory_checkbox")
if user_data["active_directory_checked"]: if user_data["active_directory_checked"]:
checkbox.check() checkbox.check()
else: else:
checkbox.uncheck() 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: if "name" in fields:
input_field = self.get_content_item("name_input") input_field = self.get_content_item("name_input")
input_field.input_value(user_data["name"]) input_field.input_value(user_data["name"])
if "role" in fields: if "role" in fields:
role_field = self.get_content_item("role_input") role_field = self.get_content_item("role_input")
role_field.click() role_field.click()
roles_list = self.get_content_item("roles_list") roles_list = self.get_content_item("roles_list")
roles_list.check_item_with_text(user_data["role"]) roles_list.check_item_with_text(user_data["role"])
roles_list.click_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 = self.get_content_item("commentary_input")
input_field.input_value(user_data["commentary"]) input_field.input_value(user_data["commentary"])
if "email" in fields: if "email" in fields:
input_field = self.get_content_item("email_input") input_field = self.get_content_item("email_input")
input_field.input_value(user_data["email"]) input_field.input_value(user_data["email"])
if "phone_number" in fields: if "phone_number" in fields:
input_field = self.get_content_item("phone_input") input_field = self.get_content_item("phone_input")
input_field.input_value(user_data["phone_number"]) input_field.input_value(user_data["phone_number"])
if "push_notification_checked" in fields: if "push_notification_checked" in fields:
checkbox = self.get_content_item("push_notification_checkbox") checkbox = self.get_content_item("push_notification_checkbox")
if user_data["push_notification_checked"]: if user_data["push_notification_checked"]:
checkbox.check() checkbox.check()
else: else:
checkbox.uncheck() checkbox.uncheck()
# Отправка формы # Отправка формы
add_button = self.get_button_by_name("add") add_button = self.get_button_by_name("add")
add_button.click() add_button.click()
# Подтверждение действия # Подтверждение действия
title = "Добавить нового пользователя" title = "Добавить нового пользователя"
self.new_user_confirm.check_title( self.new_user_confirm.check_title(
title, title,
f"Confirmation dialog window with title '{title}' is missing" f"Confirmation dialog window with title '{title}' is missing"
) )
self.new_user_confirm.click_allow_button() self.new_user_confirm.click_allow_button()
@ -189,41 +190,44 @@ class AddUserModalWindow(ModalWindowComponent):
"""Закрывает модальное окно с помощью кнопки 'Закрыть'.""" """Закрывает модальное окно с помощью кнопки 'Закрыть'."""
close_button = self.get_button_by_name("close") close_button = self.get_button_by_name("close")
close_button.click() close_button.click()
def close_window_by_toolbar_button(self): def close_window_by_toolbar_button(self):
"""Закрывает модальное окно с помощью кнопки закрытия в тулбаре.""" """Закрывает модальное окно с помощью кнопки закрытия в тулбаре."""
self.click_toolbar_close_button() self.click_toolbar_close_button()
def check_content(self): def check_content(self):
"""Проверяет наличие и корректность всех элементов модального окна. """Проверяет наличие и корректность всех элементов модального окна.
Raises: Raises:
AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные
""" """
self.check_by_window_title() self.check_by_window_title()
self.check_toolbar_button_presence("close") self.check_toolbar_button_presence("close")
self.check_toolbar_button_tooltip("close", "Закрыть") self.check_toolbar_button_tooltip("close", "Закрыть")
for name in self.content_items.keys(): for name in self.content_items.keys():
item = self.get_content_item(name) item = self.get_content_item(name)
if name == "active_directory_checkbox_label": if name == "active_directory_checkbox_label":
item.check_have_text( item.check_have_text(
"Active Directory", "Active Directory",
"Label 'Active Directory' is missing" "Label 'Active Directory' is missing"
) )
elif name == "push_notification_checkbox_label": elif name == "push_notification_checkbox_label":
item.check_have_text( item.check_have_text(
"Подписка на Push-уведомления", "Подписка на Push-уведомления",
"Label 'Подписка на Push-уведомления' is missing" "Label 'Подписка на Push-уведомления' is missing"
) )
elif name == "role_input": elif name == "role_input":
item.click() item.click()
roles_list = self.get_content_item("roles_list") roles_list = self.get_content_item("roles_list")
roles_list.check_presence("Roles list is missing") roles_list.check_presence("Roles list is missing")
for role in roles_dict.values(): for role in roles_dict.values():
# временно, пока есть несоответствие со списком ролей в вкладке Сессии
if role == "Пользователь":
continue
roles_list.check_item_with_text(role) roles_list.check_item_with_text(role)
elif name == "roles_list": elif name == "roles_list":
continue continue
@ -231,6 +235,6 @@ class AddUserModalWindow(ModalWindowComponent):
item.check_presence( item.check_presence(
f"Modal window content item with name '{name}' is missing" f"Modal window content item with name '{name}' is missing"
) )
self.check_button_presence("add") self.check_button_presence("add")
self.check_button_presence("close") self.check_button_presence("close")

View File

@ -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 import re
from playwright.sync_api import Page
from tools.logger import get_logger 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") logger = get_logger("EDIT_USER_MODAL_WINDOW")
@ -29,80 +29,76 @@ class EditUserModalWindow(ModalWindowComponent):
def __init__(self, page: Page, user_name: str): def __init__(self, page: Page, user_name: str):
"""Инициализация компонентов модального окна редактирования пользователя.""" """Инициализация компонентов модального окна редактирования пользователя."""
super().__init__(page) super().__init__(page)
# Локаторы элементов формы # Локаторы элементов формы
text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA text_field_locator = ModalWindowLocators.TEXT_FIELD_INPUT_FORM_USER_DATA
roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA roles_field_locator = ModalWindowLocators.ROLES_FIELD_INPUT_FORM_USER_DATA
input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA input_form_locator = ModalWindowLocators.INPUT_FORM_USER_DATA
label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA label_locator = ModalWindowLocators.LABEL_INPUT_FORM_USER_DATA
roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA roles_menu_locator = ModalWindowLocators.ROLES_MENU_INPUT_FORM_USER_DATA
# Настройка заголовка и кнопки закрытия # Настройка заголовка и кнопки закрытия
self.window_title = user_name self.window_title = user_name
locator_button_toolbar_close = self.page.get_by_role("navigation").filter( locator_button_toolbar_close = self.page.get_by_role("navigation").filter(
has_text=re.compile(self.window_title) has_text=re.compile(self.window_title)
).get_by_role("button") ).get_by_role("button")
self.add_toolbar_title(self.window_title) self.add_toolbar_title(self.window_title)
self.add_toolbar_button(locator_button_toolbar_close, "close") self.add_toolbar_button(locator_button_toolbar_close, "close")
# Добавление полей формы # Добавление полей формы
loc = self.page.locator(input_form_locator).locator("xpath=div[1]").locator(text_field_locator) 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)
name_input = TextInput(page, loc, "name_input") name_input = TextInput(page, loc, "name_input")
self.add_content_item("name_input", 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") role_input = TextInput(page, role_loc, "role_input")
self.add_content_item("role_input", role_input) self.add_content_item("role_input", role_input)
self.add_content_item( self.add_content_item(
"roles_list", "roles_list",
DropdownList(page, roles_menu_locator, "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") commentary_input = TextInput(page, loc, "commentary_input")
self.add_content_item("commentary_input", 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") email_input = TextInput(page, loc, "email_input")
self.add_content_item("email_input", 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") phone_input = TextInput(page, loc, "phone_input")
self.add_content_item("phone_input", phone_input) self.add_content_item("phone_input", phone_input)
# Добавление чекбоксов и их меток # Добавление чекбоксов и их меток
checkbox_2 = Checkbox( checkbox_2 = Checkbox(
page, page,
page.get_by_role("checkbox").nth(0), page.get_by_role("checkbox").nth(0),
"push_notification" "push_notification"
) )
self.add_content_item("push_notification_checkbox", checkbox_2) self.add_content_item("push_notification_checkbox", checkbox_2)
label_2 = Text( label_2 = Text(
page, page,
self.page.locator(label_locator).nth(0), self.page.locator(label_locator).nth(0),
"push_notification_checkbox_label" "push_notification_checkbox_label"
) )
self.add_content_item("push_notification_checkbox_label", label_2) self.add_content_item("push_notification_checkbox_label", label_2)
# Добавление кнопок действий # Добавление кнопок действий
locator_button_save = self.page.get_by_role("button", name="Сохранить") locator_button_save = self.page.get_by_role("button", name="Сохранить")
self.add_button(locator_button_save, "save") self.add_button(locator_button_save, "save")
locator_button_delete = self.page.get_by_role("button", name="Удалить") locator_button_delete = self.page.get_by_role("button", name="Удалить")
self.add_button(locator_button_delete, "delete") self.add_button(locator_button_delete, "delete")
locator_button_reset = self.page.get_by_role("button", name="Сбросить пароль") locator_button_reset = self.page.get_by_role("button", name="Сбросить пароль")
self.add_button(locator_button_reset, "reset_password") self.add_button(locator_button_reset, "reset_password")
locator_button_close = self.page.get_by_role("button", name="Закрыть") locator_button_close = self.page.get_by_role("button", name="Закрыть")
self.add_button(locator_button_close, "close") self.add_button(locator_button_close, "close")
# Инициализация компонентов подтверждения # Инициализация компонентов подтверждения
self.save_user_confirm = ConfirmComponent(page, " Отмена ", " Сохранить ") self.save_user_confirm = ConfirmComponent(page, " Отмена ", " Сохранить ")
self.delete_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 = self.get_button_by_name("close")
close_button.click() close_button.click()
def close_window_by_toolbar_button(self): def close_window_by_toolbar_button(self):
"""Закрывает модальное окно с помощью кнопки закрытия в тулбаре.""" """Закрывает модальное окно с помощью кнопки закрытия в тулбаре."""
self.click_toolbar_close_button() self.click_toolbar_close_button()
def delete_user(self): def delete_user(self):
"""Удаляет пользователя с подтверждением действия. """Удаляет пользователя с подтверждением действия.
Raises: Raises:
AssertionError: Если окно подтверждения не отображается AssertionError: Если окно подтверждения не отображается
""" """
delete_button = self.get_button_by_name("delete") delete_button = self.get_button_by_name("delete")
delete_button.click() delete_button.click()
title = "Удаление" title = "Удаление"
self.delete_user_confirm.check_title( self.delete_user_confirm.check_title(
title, title,
f"Confirmation dialog window with title '{title}' is missing" f"Confirmation dialog window with title '{title}' is missing"
) )
self.delete_user_confirm.click_allow_button() self.delete_user_confirm.click_allow_button()
def edit_user(self, user_data): def edit_user(self, user_data):
"""Редактирует данные пользователя. """Редактирует данные пользователя.
Args: Args:
user_data (dict): Словарь с обновляемыми данными пользователя. Может содержать: user_data (dict): Словарь с обновляемыми данными пользователя. Может содержать:
- type_auth (str): Тип авторизации
- name (str): Имя пользователя - name (str): Имя пользователя
- role (str): Роль пользователя - role (str): Роль пользователя
- commentary (str): Комментарий - commentary (str): Комментарий
@ -146,48 +141,44 @@ class EditUserModalWindow(ModalWindowComponent):
- push_notification_checked (bool): Состояние чекбокса уведомлений - push_notification_checked (bool): Состояние чекбокса уведомлений
""" """
fields = user_data.keys() 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: if "name" in fields:
input_field = self.get_content_item("name_input") input_field = self.get_content_item("name_input")
input_field.input_value(user_data["name"]) input_field.input_value(user_data["name"])
if "role" in fields: if "role" in fields:
role_field = self.get_content_item("role_input") role_field = self.get_content_item("role_input")
role_field.click() role_field.click()
roles_list = self.get_content_item("roles_list") roles_list = self.get_content_item("roles_list")
roles_list.check_item_with_text(user_data["role"]) roles_list.check_item_with_text(user_data["role"])
roles_list.click_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 = self.get_content_item("commentary_input")
input_field.input_value(user_data["commentary"]) input_field.input_value(user_data["commentary"])
if "email" in fields: if "email" in fields:
input_field = self.get_content_item("email_input") input_field = self.get_content_item("email_input")
input_field.input_value(user_data["email"]) input_field.input_value(user_data["email"])
if "phone_number" in fields: if "phone_number" in fields:
input_field = self.get_content_item("phone_input") input_field = self.get_content_item("phone_input")
input_field.input_value(user_data["phone_number"]) input_field.input_value(user_data["phone_number"])
if "push_notification_checked" in fields: if "push_notification_checked" in fields:
checkbox = self.get_content_item("push_notification_checkbox") checkbox = self.get_content_item("push_notification_checkbox")
if user_data["push_notification_checked"]: if user_data["push_notification_checked"]:
checkbox.check() checkbox.check()
else: else:
checkbox.uncheck() checkbox.uncheck()
save_button = self.get_button_by_name("save") save_button = self.get_button_by_name("save")
save_button.click() save_button.click()
title = "Сохранение" title = "Сохранение"
self.save_user_confirm.check_title( self.save_user_confirm.check_title(
title, title,
f"Confirmation dialog window with title '{title}' is missing" f"Confirmation dialog window with title '{title}' is missing"
) )
self.save_user_confirm.click_allow_button() 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 = self.get_button_by_name("reset_password")
reset_password_button.click() reset_password_button.click()
def check_content(self, user_name, role): def check_content(self, user_name, role):
"""Проверяет наличие и корректность всех элементов окна. """Проверяет наличие и корректность всех элементов окна.
Args: Args:
user_name (str): Ожидаемое имя пользователя user_name (str): Ожидаемое имя пользователя
role (str): Ожидаемая роль пользователя role (str): Ожидаемая роль пользователя
Raises: Raises:
AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные AssertionError: Если какой-либо элемент отсутствует или содержит некорректные данные
""" """
self.check_by_window_title() self.check_by_window_title()
self.check_toolbar_button_presence("close") self.check_toolbar_button_presence("close")
self.check_toolbar_button_tooltip("close", "Закрыть") self.check_toolbar_button_tooltip("close", "Закрыть")
for name in self.content_items.keys(): for name in self.content_items.keys():
item = self.get_content_item(name) item = self.get_content_item(name)
if name == "push_notification_checkbox_label": if name == "push_notification_checkbox_label":
item.check_have_text( item.check_have_text(
"Подписка на Push-уведомления", "Подписка на Push-уведомления",
"Label 'Подписка на Push-уведомления' is missing" "Label 'Подписка на Push-уведомления' is missing"
) )
elif name == "name_input": elif name == "name_input":
@ -236,8 +227,8 @@ class EditUserModalWindow(ModalWindowComponent):
item.check_presence( item.check_presence(
f"Modal window content item with name '{name}' is missing" f"Modal window content item with name '{name}' is missing"
) )
self.check_button_presence("save") self.check_button_presence("save")
self.check_button_presence("delete") self.check_button_presence("delete")
self.check_button_presence("reset_password") self.check_button_presence("reset_password")
self.check_button_presence("close") self.check_button_presence("close")

View File

@ -1,10 +1,10 @@
"""Базовый класс страницы для работы с Playwright.""" """Базовый класс страницы для работы с 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 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") logger = get_logger("BASE_PAGE")
@ -82,4 +82,4 @@ class BasePage:
return False return False
return True return True
assert compare_lists(actual, expected), msg assert compare_lists(actual, expected), msg

View File

@ -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 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): class LicenseTab(BasePage):
"""Класс для работы с вкладкой 'Лицензии'. """Класс для работы с вкладкой 'Лицензии'.
Атрибуты: Атрибуты:
page (Page): Экземпляр страницы Playwright. page (Page): Экземпляр страницы Playwright.
toolbar (ToolbarComponent): Компонент панели инструментов. toolbar (ToolbarComponent): Компонент панели инструментов.
@ -28,15 +27,15 @@ class LicenseTab(BasePage):
def __init__(self, page: Page) -> None: def __init__(self, page: Page) -> None:
"""Инициализирует элементы вкладки 'Лицензии'. """Инициализирует элементы вкладки 'Лицензии'.
Args: Args:
page: Экземпляр страницы Playwright. page: Экземпляр страницы Playwright.
""" """
super().__init__(page) super().__init__(page)
self.toolbar = ToolbarComponent(page, "Лицензии") self.toolbar = ToolbarComponent(page, "Лицензии")
self.json_container = JsonContainerComponent(page) self.json_container = JsonContainerComponent(page)
self.input_form_title = Text(page, TextLocators.TITLE_LICENSE_INPUT_FORM, "input form title") 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 = Text(page, TextLocators.LICENSE_ID, "license id")
self.license_id_input = TextInput(page, InputLocators.LICENSE_ID_UPDATE, "license id input") 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: def fill_license_input_form(self, value: str) -> None:
"""Заполняет форму ввода идентификатора лицензии и нажимает кнопку обновления. """Заполняет форму ввода идентификатора лицензии и нажимает кнопку обновления.
Args: Args:
value: Значение для ввода в поле идентификатора лицензии. value: Значение для ввода в поле идентификатора лицензии.
""" """
self.license_id_input.clear() self.license_id_input.clear()
self.license_id_input.input_value(value) self.license_id_input.input_value(value)
self.update_button.click() self.update_button.click()
def scroll_json_container_up(self) -> None: def scroll_json_container_up(self) -> None:
"""Прокручивает JSON-контейнер вверх.""" """Прокручивает JSON-контейнер вверх."""
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first
self.json_container.scroll_up(loc) self.json_container.scroll_up(loc)
def scroll_json_container_down(self) -> None: def scroll_json_container_down(self) -> None:
"""Прокручивает JSON-контейнер вниз.""" """Прокручивает JSON-контейнер вниз."""
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first
self.json_container.scroll_down(loc) self.json_container.scroll_down(loc)
# Проверки: # Проверки:
def check_json_container_verticall_scrolling(self) -> bool: def check_json_container_verticall_scrolling(self) -> bool:
"""Проверяет возможность вертикальной прокрутки JSON-контейнера. """Проверяет возможность вертикальной прокрутки JSON-контейнера.
Returns: Returns:
bool: True если контейнер можно прокручивать, иначе False. bool: True если контейнер можно прокручивать, иначе False.
""" """
loc = self.page.locator(JsonContainerLocators.SCROLL_CONTAINER).first 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: def check_content(self) -> None:
"""Проверяет наличие всех основных элементов на вкладке.""" """Проверяет наличие всех основных элементов на вкладке."""
self.should_be_toolbar() self.should_be_toolbar()
@ -82,50 +81,50 @@ class LicenseTab(BasePage):
self.should_be_input_form_title() self.should_be_input_form_title()
self.should_be_empty_input_form() self.should_be_empty_input_form()
self.should_be_update_button() self.should_be_update_button()
def should_be_error_alert_window_with_text(self, text: str) -> None: def should_be_error_alert_window_with_text(self, text: str) -> None:
"""Проверяет наличие и отсутствие алерта с указанным текстом. """Проверяет наличие и отсутствие алерта с указанным текстом.
Args: Args:
text: Текст для проверки в алерте. text: Текст для проверки в алерте.
""" """
self.error_alert.check_presence(text) self.error_alert.check_alert_presence(text)
self.error_alert.check_absence(text) self.error_alert.check_alert_absence(text)
def should_be_toolbar(self) -> None: 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: def should_be_json_container(self) -> None:
"""Проверяет наличие JSON-контейнера с информацией о лицензии.""" """Проверяет наличие JSON-контейнера с информацией о лицензии."""
self.json_container.check_presence( self.json_container.check_presence(
JsonContainerLocators.CONTAINER, JsonContainerLocators.CONTAINER,
"Json container with license info is missing" "Json container with license info is missing"
) )
def should_be_input_form_title(self) -> None: def should_be_input_form_title(self) -> None:
"""Проверяет заголовок формы ввода и соответствие ID лицензии.""" """Проверяет заголовок формы ввода и соответствие ID лицензии."""
self.input_form_title.check_have_text( self.input_form_title.check_have_text(
"Идентификатор:", "Идентификатор:",
"Input lisence id form title 'Идентификатор:' is missing" "Input lisence id form title 'Идентификатор:' is missing"
) )
actual_lisence_id = self.license_id.get_text(0).strip() actual_lisence_id = self.license_id.get_text(0).strip()
# send request to backend to get license id # send request to backend to get license id
response = self.send_get_api_request("e-cmdb/api/lic/deviceid") response = self.send_get_api_request("e-cmdb/api/lic/deviceid")
response_body = self.get_response_body(response) response_body = self.get_response_body(response)
self.check_equals( self.check_equals(
actual_lisence_id, actual_lisence_id,
response_body['deviceId'], response_body['deviceId'],
f"Expected ID value {response_body['deviceId']} is not equal actual value {actual_lisence_id}" f"Expected ID value {response_body['deviceId']} is not equal actual value {actual_lisence_id}"
) )
def should_be_empty_input_form(self) -> None: def should_be_empty_input_form(self) -> None:
"""Проверяет, что форма ввода идентификатора лицензии пуста.""" """Проверяет, что форма ввода идентификатора лицензии пуста."""
self.license_id_input.check_empty_input("Input lisence id form is missing or not empty") self.license_id_input.check_empty_input("Input lisence id form is missing or not empty")
def should_be_update_button(self) -> None: def should_be_update_button(self) -> None:
"""Проверяет наличие кнопки обновления лицензии с правильным текстом.""" """Проверяет наличие кнопки обновления лицензии с правильным текстом."""
button_text = "Обновить лицензию" button_text = "Обновить лицензию"
@ -133,7 +132,7 @@ class LicenseTab(BasePage):
button_text, button_text,
f"Update button with text '{button_text}' is missing" f"Update button with text '{button_text}' is missing"
) )
def verify_json_container_content(self) -> None: def verify_json_container_content(self) -> None:
"""Проверяет соответствие содержимого JSON-контейнера данным из API.""" """Проверяет соответствие содержимого JSON-контейнера данным из API."""
actual_data = self.json_container.read_data(JsonContainerLocators.CONTAINER) actual_data = self.json_container.read_data(JsonContainerLocators.CONTAINER)
@ -141,13 +140,13 @@ class LicenseTab(BasePage):
# send request to backend to get license info # send request to backend to get license info
response = self.send_get_api_request("e-cmdb/api/lic") response = self.send_get_api_request("e-cmdb/api/lic")
response_body = self.get_response_body(response) response_body = self.get_response_body(response)
## temporarily ## temporarily
del response_body["netManagment"] del response_body["netManagment"]
response_body["ui"].pop("lcc") response_body["ui"].pop("lcc")
self.json_container.check_json_equals( self.json_container.check_json_equals(
actual_data, actual_data,
response_body, response_body,
"Expected json content is not equal actual:" "Expected json content is not equal actual:"
) )

View File

@ -1,17 +1,14 @@
from playwright.sync_api import Page from playwright.sync_api import Page
from elements.button_element import Button
from elements.text_input_element import TextInput 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 components.alert_component import AlertComponent
from pages.base_page import BasePage from pages.base_page import BasePage
from data.constants import Constants
from data.environment import host
class LoginPage(BasePage): class LoginPage(BasePage):
"""Класс для работы со страницей авторизации. """Класс для работы со страницей авторизации.
Атрибуты: Атрибуты:
page (Page): Экземпляр страницы Playwright. page (Page): Экземпляр страницы Playwright.
login_input (TextInput): Поле ввода логина. login_input (TextInput): Поле ввода логина.
@ -22,28 +19,28 @@ class LoginPage(BasePage):
def __init__(self, page: Page) -> None: def __init__(self, page: Page) -> None:
"""Инициализирует элементы страницы авторизации. """Инициализирует элементы страницы авторизации.
Args: Args:
page: Экземпляр страницы Playwright. page: Экземпляр страницы Playwright.
""" """
super().__init__(page) super().__init__(page)
self.login_input = TextInput(page, page.get_by_label("Имя пользователя"), "login input") self.login_input = TextInput(page, page.get_by_label("Имя пользователя"), "login input")
self.password_input = TextInput(page, page.get_by_label("Пароль"), "password 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.login_button = Button(page, page.get_by_role("button"), "login button")
self.error_alert = AlertComponent(page, "error") self.error_alert = AlertComponent(page, "error")
def do_login(self, username: str = None, password: str = None) -> None: def do_login(self, username: str = None, password: str = None) -> None:
"""Выполняет вход в систему. """Выполняет вход в систему.
Если username/password не указаны, использует значения из Constants. Если username/password не указаны, использует значения из Constants.
Обрабатывает ответ сервера для получения токена доступа. Обрабатывает ответ сервера для получения токена доступа.
Args: Args:
username: Логин пользователя. Если None, используется значение из Constants. username: Логин пользователя. Если None, используется значение из Constants.
password: Пароль пользователя. Если None, используется значение из Constants. password: Пароль пользователя. Если None, используется значение из Constants.
Raises: Raises:
AssertionError: Если после входа открылась неожиданная страница. AssertionError: Если после входа открылась неожиданная страница.
""" """
@ -53,44 +50,44 @@ class LoginPage(BasePage):
if response_body: if response_body:
token = response_body.get("access_token") token = response_body.get("access_token")
host.set_access_token(token) host.set_access_token(token)
self.page.on("response", handle_response) self.page.on("response", handle_response)
self.open("") self.open("")
# Используем переданные значения или значения по умолчанию из Constants # Используем переданные значения или значения по умолчанию из Constants
actual_username = username if username is not None else Constants.login actual_username = username if username is not None else Constants.login
actual_password = password if password is not None else Constants.password actual_password = password if password is not None else Constants.password
self.login_input.clear() self.login_input.clear()
self.login_input.input_value(actual_username) self.login_input.input_value(actual_username)
self.password_input.clear() self.password_input.clear()
self.password_input.input_value(actual_password) self.password_input.input_value(actual_password)
self.login_button.click() self.login_button.click()
self.check_URL("dashboard", "An unexpected page has been opened") self.check_URL("dashboard", "An unexpected page has been opened")
def do_unsuccessful_login(self, username: str = "someuser", password: str = "password") -> None: def do_unsuccessful_login(self, username: str = "someuser", password: str = "password") -> None:
"""Выполняет попытку входа с неверными учетными данными. """Выполняет попытку входа с неверными учетными данными.
Можно передать свои неверные данные или использовать значения по умолчанию. Можно передать свои неверные данные или использовать значения по умолчанию.
Проверяет наличие сообщения об ошибке. Проверяет наличие сообщения об ошибке.
Args: Args:
username: Неверный логин пользователя. По умолчанию "someuser". username: Неверный логин пользователя. По умолчанию "someuser".
password: Неверный пароль пользователя. По умолчанию "password". password: Неверный пароль пользователя. По умолчанию "password".
""" """
self.open("") self.open("")
self.login_input.clear() self.login_input.clear()
self.login_input.input_value(username) self.login_input.input_value(username)
self.password_input.clear() self.password_input.clear()
self.password_input.input_value(password) self.password_input.input_value(password)
self.login_button.click() self.login_button.click()
self.error_alert.check_presence("Неверная пара логин/пароль") self.error_alert.check_alert_presence("Неверная пара логин/пароль")
self.error_alert.check_absence("Неверная пара логин/пароль") self.error_alert.check_alert_absence("Неверная пара логин/пароль")

View File

@ -1,15 +1,14 @@
from pages.base_page import BasePage from playwright.sync_api import Page
from elements.button_element import Button
from components.card_component import CardComponent
from components.navbar_component import NavigationPanelComponent
from locators.navigation_panel_locators import NavigationPanelLocators from locators.navigation_panel_locators import NavigationPanelLocators
from locators.event_panel_locators import EventPanelLocators 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): class MainPage(BasePage):
"""Класс для работы с главной страницей приложения. """Класс для работы с главной страницей приложения.
Атрибуты: Атрибуты:
page (Page): Экземпляр страницы Playwright. page (Page): Экземпляр страницы Playwright.
navigation_panel (NavigationPanelComponent): Компонент панели навигации. navigation_panel (NavigationPanelComponent): Компонент панели навигации.
@ -19,63 +18,63 @@ class MainPage(BasePage):
def __init__(self, page: Page) -> None: def __init__(self, page: Page) -> None:
"""Инициализирует элементы главной страницы. """Инициализирует элементы главной страницы.
Args: Args:
page: Экземпляр страницы Playwright. page: Экземпляр страницы Playwright.
""" """
super().__init__(page) super().__init__(page)
self.navigation_panel = NavigationPanelComponent(page) self.navigation_panel = NavigationPanelComponent(page)
locators = self.page.locator(EventPanelLocators.BUTTONS_BLOCK).get_by_role("button").all() 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[0], "search_button")
self.user_button = Button(page, locators[1], "user_button") self.user_button = Button(page, locators[1], "user_button")
self.user_card = CardComponent(page) self.user_card = CardComponent(page)
# Действия: # Действия:
def click_main_navigation_panel_item(self, item_name: str) -> None: def click_main_navigation_panel_item(self, item_name: str) -> None:
"""Кликает по элементу основной панели навигации. """Кликает по элементу основной панели навигации.
Args: Args:
item_name: Название элемента для клика. item_name: Название элемента для клика.
""" """
self.navigation_panel.click_item(NavigationPanelLocators.PANEL_MAIN, item_name) self.navigation_panel.click_item(NavigationPanelLocators.PANEL_MAIN, item_name)
def click_configuration_navigation_panel_item(self, item_name: str) -> None: def click_configuration_navigation_panel_item(self, item_name: str) -> None:
"""Кликает по элементу подраздела 'Конфигурация' в панели навигации. """Кликает по элементу подраздела 'Конфигурация' в панели навигации.
Args: Args:
item_name: Название элемента для клика. item_name: Название элемента для клика.
""" """
self.navigation_panel.click_sub_item(NavigationPanelLocators.PANEL_MAIN, 1, 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: def click_maintenance_navigation_panel_item(self, item_name: str) -> None:
"""Кликает по элементу подраздела 'Обслуживание' в панели навигации. """Кликает по элементу подраздела 'Обслуживание' в панели навигации.
Args: Args:
item_name: Название элемента для клика. item_name: Название элемента для клика.
""" """
self.navigation_panel.click_sub_item(NavigationPanelLocators.PANEL_MAIN, 2, item_name) self.navigation_panel.click_sub_item(NavigationPanelLocators.PANEL_MAIN, 2, item_name)
def click_user_button(self) -> None: def click_user_button(self) -> None:
"""Кликает по кнопке пользователя.""" """Кликает по кнопке пользователя."""
self.user_button.click() self.user_button.click()
def do_logout(self) -> None: def do_logout(self) -> None:
"""Выполняет выход из системы.""" """Выполняет выход из системы."""
self.should_be_user_button() self.should_be_user_button()
self.click_user_button() self.click_user_button()
self.user_card.click_logout_button() self.user_card.click_logout_button()
def scroll_navigation_panel_up(self) -> None: def scroll_navigation_panel_up(self) -> None:
"""Прокручивает панель навигации вверх.""" """Прокручивает панель навигации вверх."""
self.navigation_panel.scroll_up(NavigationPanelLocators.PANEL_SCROLL_CONTAINER) self.navigation_panel.scroll_up(NavigationPanelLocators.PANEL_SCROLL_CONTAINER)
def scroll_navigation_panel_down(self) -> None: def scroll_navigation_panel_down(self) -> None:
"""Прокручивает панель навигации вниз.""" """Прокручивает панель навигации вниз."""
self.navigation_panel.scroll_down(NavigationPanelLocators.PANEL_SCROLL_CONTAINER) self.navigation_panel.scroll_down(NavigationPanelLocators.PANEL_SCROLL_CONTAINER)
# Проверки: # Проверки:
def should_be_navigation_panel(self) -> None: def should_be_navigation_panel(self) -> None:
"""Проверяет наличие панели навигации.""" """Проверяет наличие панели навигации."""
@ -83,28 +82,28 @@ class MainPage(BasePage):
NavigationPanelLocators.PANEL_MAIN, NavigationPanelLocators.PANEL_MAIN,
"Navigation panel is missing" "Navigation panel is missing"
) )
def should_be_user_button(self) -> None: def should_be_user_button(self) -> None:
"""Проверяет наличие кнопки пользователя.""" """Проверяет наличие кнопки пользователя."""
self.user_button.check_presence("User button is missing on event panel") self.user_button.check_presence("User button is missing on event panel")
def check_navigation_panel_verticall_scrolling(self) -> bool: def check_navigation_panel_verticall_scrolling(self) -> bool:
"""Проверяет возможность вертикальной прокрутки панели навигации. """Проверяет возможность вертикальной прокрутки панели навигации.
Returns: Returns:
bool: True если панель можно прокручивать, иначе False. bool: True если панель можно прокручивать, иначе False.
""" """
return self.navigation_panel.is_scrollable_vertically( return self.navigation_panel.is_scrollable_vertically(
NavigationPanelLocators.PANEL_SCROLL_CONTAINER NavigationPanelLocators.PANEL_SCROLL_CONTAINER
) )
def check_navigation_panel_item_visibility(self, item_name: str) -> None: def check_navigation_panel_item_visibility(self, item_name: str) -> None:
"""Проверяет видимость элемента в панели навигации. """Проверяет видимость элемента в панели навигации.
Args: Args:
item_name: Название элемента для проверки. item_name: Название элемента для проверки.
""" """
self.navigation_panel.check_item_visibility( self.navigation_panel.check_item_visibility(
NavigationPanelLocators.PANEL_MAIN, NavigationPanelLocators.PANEL_MAIN,
item_name item_name
) )

View File

@ -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.toolbar_component import ToolbarComponent
from components.table_component import TableComponent from components.table_component import TableComponent
from locators.table_locators import TableLocators from pages.base_page import BasePage
from playwright.sync_api import Page
class ServiceStatusTab(BasePage): class ServiceStatusTab(BasePage):
@ -17,126 +17,126 @@ class ServiceStatusTab(BasePage):
def __init__(self, page: Page) -> None: def __init__(self, page: Page) -> None:
"""Инициализация компонентов вкладки 'Статус обслуживания'.""" """Инициализация компонентов вкладки 'Статус обслуживания'."""
super().__init__(page) super().__init__(page)
self.toolbar = ToolbarComponent(page, "Статус обслуживания") self.toolbar = ToolbarComponent(page, "Статус обслуживания")
self.services_table = TableComponent(page) self.services_table = TableComponent(page)
def get_rows_count(self) -> int: def get_rows_count(self) -> int:
"""Возвращает количество строк в таблице сервисов (без учёта заголовка). """Возвращает количество строк в таблице сервисов (без учёта заголовка).
Returns: Returns:
int: Количество строк с данными. int: Количество строк с данными.
Raises: Raises:
AssertionError: Если таблица пуста. AssertionError: Если таблица пуста.
""" """
table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA)
rows_count = len(table_content) rows_count = len(table_content)
if rows_count == 0: if rows_count == 0:
assert False, "The contents of the table are missing" assert False, "The contents of the table are missing"
return rows_count - 1 return rows_count - 1
def scroll_services_table_up(self) -> None: def scroll_services_table_up(self) -> None:
"""Прокручивает таблицу сервисов вверх.""" """Прокручивает таблицу сервисов вверх."""
self.services_table.scroll_up(TableLocators.TABLE_SCROLL_CONTAINER) self.services_table.scroll_up(TableLocators.TABLE_SCROLL_CONTAINER)
def scroll_services_table_down(self) -> None: def scroll_services_table_down(self) -> None:
"""Прокручивает таблицу сервисов вниз.""" """Прокручивает таблицу сервисов вниз."""
self.services_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER) self.services_table.scroll_down(TableLocators.TABLE_SCROLL_CONTAINER)
def check_services_table_content(self) -> None: def check_services_table_content(self) -> None:
"""Проверяет содержимое таблицы сервисов. """Проверяет содержимое таблицы сервисов.
Проверяет: Проверяет:
- Наличие заголовков таблицы - Наличие заголовков таблицы
- Соответствие заголовков ожидаемым значениям - Соответствие заголовков ожидаемым значениям
- Наличие хотя бы одной строки с данными - Наличие хотя бы одной строки с данными
Raises: Raises:
AssertionError: Если таблица пуста или заголовки не соответствуют ожидаемым. AssertionError: Если таблица пуста или заголовки не соответствуют ожидаемым.
""" """
expected_headers = [ expected_headers = [
'Контейнер', 'Контейнер',
'Время создания', 'Время создания',
'Статус', 'Статус',
'Время работы', 'Время работы',
'Image ID', 'Image ID',
'Image ТЭГ' 'Image ТЭГ'
] ]
table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.services_table.read(TableLocators.TABLE_WORK_AREA)
if len(table_content) == 0: if len(table_content) == 0:
assert False, "The contents of the table are missing" assert False, "The contents of the table are missing"
actual_headers = table_content[0] actual_headers = table_content[0]
self.check_equals( self.check_equals(
actual_headers, actual_headers,
expected_headers, expected_headers,
f"Expected table headers {expected_headers} are not equal {actual_headers}" f"Expected table headers {expected_headers} are not equal {actual_headers}"
) )
if len(table_content) == 1: if len(table_content) == 1:
assert False, "Table body is missing" assert False, "Table body is missing"
def check_services_table_verticall_scrolling(self) -> bool: def check_services_table_verticall_scrolling(self) -> bool:
"""Проверяет возможность вертикальной прокрутки таблицы. """Проверяет возможность вертикальной прокрутки таблицы.
Returns: Returns:
bool: True если прокрутка возможна, иначе False. bool: True если прокрутка возможна, иначе False.
""" """
return self.services_table.is_scrollable_vertically( return self.services_table.is_scrollable_vertically(
TableLocators.TABLE_SCROLL_CONTAINER TableLocators.TABLE_SCROLL_CONTAINER
) )
def check_services_table_first_row_visibility(self) -> None: def check_services_table_first_row_visibility(self) -> None:
"""Проверяет видимость первой строки таблицы. """Проверяет видимость первой строки таблицы.
Raises: Raises:
AssertionError: Если первая строка не видна. AssertionError: Если первая строка не видна.
""" """
self.services_table.check_first_row_visibility(TableLocators.TABLE_WORK_AREA) self.services_table.check_first_row_visibility(TableLocators.TABLE_WORK_AREA)
def check_services_table_last_row_visibility(self) -> None: def check_services_table_last_row_visibility(self) -> None:
"""Проверяет видимость последней строки таблицы. """Проверяет видимость последней строки таблицы.
Raises: Raises:
AssertionError: Если последняя строка не видна. AssertionError: Если последняя строка не видна.
""" """
self.services_table.check_last_row_visibility(TableLocators.TABLE_WORK_AREA) self.services_table.check_last_row_visibility(TableLocators.TABLE_WORK_AREA)
def check_services_table_row_highlighting(self, row_index: int) -> None: def check_services_table_row_highlighting(self, row_index: int) -> None:
"""Проверяет выделение указанной строки таблицы. """Проверяет выделение указанной строки таблицы.
Args: Args:
row_index (int): Индекс проверяемой строки. row_index (int): Индекс проверяемой строки.
Raises: Raises:
AssertionError: Если строка не выделена. AssertionError: Если строка не выделена.
""" """
self.services_table.check_row_highlighting( self.services_table.check_row_highlighting(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
row_index row_index
) )
def should_be_toolbar(self) -> None: def should_be_toolbar(self) -> None:
"""Проверяет наличие тулбара на вкладке. """Проверяет наличие тулбара на вкладке.
Raises: Raises:
AssertionError: Если тулбар отсутствует. AssertionError: Если тулбар отсутствует.
""" """
self.toolbar.check_presence("Toolbar is missing") self.toolbar.check_toolbar_presence("Toolbar is missing")
def should_be_services_table(self) -> None: def should_be_services_table(self) -> None:
"""Проверяет наличие таблицы сервисов. """Проверяет наличие таблицы сервисов.
Raises: Raises:
AssertionError: Если таблица отсутствует. AssertionError: Если таблица отсутствует.
""" """
self.services_table.check_presence( self.services_table.check_presence(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
"Service statuses table is missing" "Service statuses table is missing"
) )

View File

@ -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 elements.tooltip_button_element import TooltipButton
from data.roles_dict import roles_dict
from components.toolbar_component import ToolbarComponent from components.toolbar_component import ToolbarComponent
from components.table_component import TableComponent from components.table_component import TableComponent
from locators.button_locators import ButtonLocators from pages.base_page import BasePage
from locators.table_locators import TableLocators
from playwright.sync_api import Page, Locator
from data.roles_dict import roles_dict
class SessionsTab(BasePage): class SessionsTab(BasePage):
"""Класс для работы с вкладкой 'Сессия'. """Класс для работы с вкладкой 'Сессия'.
@ -20,7 +19,7 @@ class SessionsTab(BasePage):
def __init__(self, page: Page) -> None: def __init__(self, page: Page) -> None:
"""Инициализация компонентов вкладки 'Сессия'.""" """Инициализация компонентов вкладки 'Сессия'."""
super().__init__(page) super().__init__(page)
self.toolbar = ToolbarComponent(page, "Сессия") self.toolbar = ToolbarComponent(page, "Сессия")
self.sessions_table = TableComponent(page) self.sessions_table = TableComponent(page)
@ -29,16 +28,16 @@ class SessionsTab(BasePage):
Returns: Returns:
int: Количество строк с данными. int: Количество строк с данными.
Raises: Raises:
AssertionError: Если таблица пуста. AssertionError: Если таблица пуста.
""" """
table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA)
rows_count = len(table_content) rows_count = len(table_content)
if rows_count == 0: if rows_count == 0:
assert False, "The contents of the table are missing" assert False, "The contents of the table are missing"
return rows_count - 1 return rows_count - 1
def get_delete_session_button_from_row(self, row_index: int) -> TooltipButton: def get_delete_session_button_from_row(self, row_index: int) -> TooltipButton:
@ -54,11 +53,11 @@ class SessionsTab(BasePage):
AssertionError: Если строка не найдена. AssertionError: Если строка не найдена.
""" """
row_locator = self.sessions_table.get_row_locator( row_locator = self.sessions_table.get_row_locator(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
row_index row_index
) )
assert isinstance(row_locator, Locator), f"Row with index {row_index} is missing" assert isinstance(row_locator, Locator), f"Row with index {row_index} is missing"
button_locator = row_locator.locator(ButtonLocators.BUTTON_DELETE_SESSION) button_locator = row_locator.locator(ButtonLocators.BUTTON_DELETE_SESSION)
return TooltipButton(self.page, button_locator, "delete_session_button") return TooltipButton(self.page, button_locator, "delete_session_button")
@ -80,33 +79,33 @@ class SessionsTab(BasePage):
AssertionError: Если таблица пуста или заголовки не соответствуют. AssertionError: Если таблица пуста или заголовки не соответствуют.
""" """
expected_headers = [ expected_headers = [
'ID сессии', 'ID сессии',
'ID пользователя', 'ID пользователя',
'Время жизни', 'Время жизни',
'Роль', 'Роль',
'Адрес' 'Адрес'
] ]
table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.sessions_table.read(TableLocators.TABLE_WORK_AREA)
len_table_content = len(table_content) len_table_content = len(table_content)
if len_table_content == 0: if len_table_content == 0:
assert False, "The contents of the table are missing" assert False, "The contents of the table are missing"
actual_headers = table_content[0] actual_headers = table_content[0]
self.check_equals( self.check_equals(
actual_headers, actual_headers,
expected_headers, expected_headers,
f"Expected table headers {expected_headers} are not equal {actual_headers}" f"Expected table headers {expected_headers} are not equal {actual_headers}"
) )
if len_table_content == 1: if len_table_content == 1:
assert False, "Table body is missing" assert False, "Table body is missing"
if verify: if verify:
self.verify_sessions_table_content(table_content) self.verify_sessions_table_content(table_content)
for index in range(len_table_content - 1): for index in range(len_table_content - 1):
self.should_be_delete_button_on_sessions_table_row(index, "Удалить") self.should_be_delete_button_on_sessions_table_row(index, "Удалить")
@ -146,7 +145,7 @@ class SessionsTab(BasePage):
AssertionError: Если строка не выделена. AssertionError: Если строка не выделена.
""" """
self.sessions_table.check_row_highlighting( self.sessions_table.check_row_highlighting(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
row_index row_index
) )
@ -156,7 +155,7 @@ class SessionsTab(BasePage):
Raises: Raises:
AssertionError: Если тулбар отсутствует. AssertionError: Если тулбар отсутствует.
""" """
self.toolbar.check_presence("Toolbar is missing") self.toolbar.check_toolbar_presence("Toolbar is missing")
def should_be_sessions_table(self) -> None: def should_be_sessions_table(self) -> None:
"""Проверяет наличие таблицы сессий. """Проверяет наличие таблицы сессий.
@ -165,13 +164,13 @@ class SessionsTab(BasePage):
AssertionError: Если таблица отсутствует. AssertionError: Если таблица отсутствует.
""" """
self.sessions_table.check_presence( self.sessions_table.check_presence(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
"Sessions table is missing" "Sessions table is missing"
) )
def should_be_delete_button_on_sessions_table_row( def should_be_delete_button_on_sessions_table_row(
self, self,
row_index: int, row_index: int,
tooltip: str tooltip: str
) -> None: ) -> None:
"""Проверяет наличие кнопки удаления в строке таблицы. """Проверяет наличие кнопки удаления в строке таблицы.
@ -199,19 +198,19 @@ class SessionsTab(BasePage):
AssertionError: Если данные не соответствуют. AssertionError: Если данные не соответствуют.
""" """
expected_sessions_list = [] expected_sessions_list = []
# Отправка запроса к бэкенду для получения информации о сессиях # Отправка запроса к бэкенду для получения информации о сессиях
response = self.send_get_api_request("e-nms/auth/sessions") response = self.send_get_api_request("e-nms/auth/sessions")
response_body = self.get_response_body(response) response_body = self.get_response_body(response)
for item in response_body: for item in response_body:
session_info = [] session_info = []
session_info.append(item["id"]) session_info.append(item["id"])
session_info.append(item["userId"]) session_info.append(item["userId"])
# Временно неподдерживаемое поле: время жизни сессии # Временно неподдерживаемое поле: время жизни сессии
session_info.append("") session_info.append("")
roles = [] roles = []
for role in item["roles"]: for role in item["roles"]:
if role in roles_dict.keys(): if role in roles_dict.keys():
@ -219,13 +218,13 @@ class SessionsTab(BasePage):
session_info.append(",".join(roles)) session_info.append(",".join(roles))
session_info.append(item["ip"]) session_info.append(item["ip"])
expected_sessions_list.append(session_info) expected_sessions_list.append(session_info)
del sessions_table[0] # Удаляем заголовок del sessions_table[0] # Удаляем заголовок
self.check_lists_equals( self.check_lists_equals(
sessions_table, sessions_table,
expected_sessions_list, expected_sessions_list,
"Actual sessions list is not equal expected users list on base db" "Actual sessions list is not equal expected users list on base db"
) )

View File

@ -1,14 +1,13 @@
from pages.base_page import BasePage import re
from components.alert_component import AlertComponent from playwright.sync_api import Page
from components.toolbar_component import ToolbarComponent
from components.table_component import TableComponent
from modal_windows.modal_add_user import AddUserModalWindow
from modal_windows.modal_edit_user import EditUserModalWindow from modal_windows.modal_edit_user import EditUserModalWindow
from modal_windows.modal_add_user import AddUserModalWindow
from locators.table_locators import TableLocators from locators.table_locators import TableLocators
from data.roles_dict import roles_dict from data.roles_dict import roles_dict
from playwright.sync_api import Page from components.toolbar_component import ToolbarComponent
import re from components.table_component import TableComponent
from components.alert_component import AlertComponent
from pages.base_page import BasePage
class UsersTab(BasePage): class UsersTab(BasePage):
"""Класс для работы с вкладкой 'Пользователи'. """Класс для работы с вкладкой 'Пользователи'.
@ -30,12 +29,12 @@ class UsersTab(BasePage):
locator_button_2 = self.page.get_by_role("navigation").filter( locator_button_2 = self.page.get_by_role("navigation").filter(
has_text=re.compile("Пользователи") has_text=re.compile("Пользователи")
).get_by_role("button").nth(1) ).get_by_role("button").nth(1)
self.toolbar = ToolbarComponent(page, "Пользователи") self.toolbar = ToolbarComponent(page, "Пользователи")
self.toolbar.add_button(locator_button_1, "edit") self.toolbar.add_button(locator_button_1, "edit")
self.toolbar.add_button(locator_button_1, "add_user") self.toolbar.add_button(locator_button_1, "add_user")
self.toolbar.add_button(locator_button_2, "close") self.toolbar.add_button(locator_button_2, "close")
self.users_table = TableComponent(page) self.users_table = TableComponent(page)
self.modal_windows = {} self.modal_windows = {}
self.success_alert = AlertComponent(page, "success") self.success_alert = AlertComponent(page, "success")
@ -51,9 +50,9 @@ class UsersTab(BasePage):
AssertionError: Если указан неподдерживаемый тип окна. AssertionError: Если указан неподдерживаемый тип окна.
""" """
if window_type == "add_user": 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": elif window_type == "edit_user":
self.modal_windows[title] = EditUserModalWindow(self.page, title) self.modal_windows[title] = EditUserModalWindow(self.page, title)
else: else:
assert False, "Unsupported modal window type" assert False, "Unsupported modal window type"
@ -141,8 +140,8 @@ class UsersTab(BasePage):
AssertionError: Если не отображается сообщение об успешном добавлении. AssertionError: Если не отображается сообщение об успешном добавлении.
""" """
self.get_modal_window("add_user").new_user(user_data) self.get_modal_window("add_user").new_user(user_data)
self.success_alert.check_presence(' Новый пользователь \n успешно добавлен! ') self.success_alert.check_alert_presence(' Новый пользователь \n успешно добавлен! ')
self.success_alert.check_absence(' Новый пользователь \n успешно добавлен! ') self.success_alert.check_alert_absence(' Новый пользователь \n успешно добавлен! ')
def delete_user(self, user_name: str) -> None: def delete_user(self, user_name: str) -> None:
"""Удаляет пользователя. """Удаляет пользователя.
@ -154,8 +153,8 @@ class UsersTab(BasePage):
AssertionError: Если не отображается сообщение об успешном удалении. AssertionError: Если не отображается сообщение об успешном удалении.
""" """
self.get_modal_window(user_name).delete_user() self.get_modal_window(user_name).delete_user()
self.success_alert.check_presence('\nПользователь удалён\n') self.success_alert.check_alert_presence('\nПользователь удалён\n')
self.success_alert.check_absence('\nПользователь удалён\n') self.success_alert.check_alert_absence('\nПользователь удалён\n')
def edit_user(self, user_name: str, user_data: dict) -> None: def edit_user(self, user_name: str, user_data: dict) -> None:
"""Редактирует данные пользователя. """Редактирует данные пользователя.
@ -168,8 +167,8 @@ class UsersTab(BasePage):
AssertionError: Если не отображается сообщение об успешном обновлении. AssertionError: Если не отображается сообщение об успешном обновлении.
""" """
self.get_modal_window(user_name).edit_user(user_data) self.get_modal_window(user_name).edit_user(user_data)
self.success_alert.check_presence('\nОбновление успешно\n') self.success_alert.check_alert_presence('\nОбновление успешно\n')
self.success_alert.check_absence('\nОбновление успешно\n') self.success_alert.check_alert_absence('\nОбновление успешно\n')
def reset_password(self, user_name: str) -> str: def reset_password(self, user_name: str) -> str:
"""Сбрасывает пароль пользователя. """Сбрасывает пароль пользователя.
@ -182,12 +181,12 @@ class UsersTab(BasePage):
""" """
new_password = "" new_password = ""
self.get_modal_window(user_name).reset_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() alert_message = self.success_alert.get_text()
if len(alert_message) > 0: if len(alert_message) > 0:
new_password = re.findall(r'[\d]+', alert_message)[0] new_password = re.findall(r'[\d]+', alert_message)[0]
return new_password return new_password
def find_user_in_table(self, name: str, role: str) -> int: 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) table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
if len(table_content) == 0: 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] # Удаляем заголовок del table_content[0] # Удаляем заголовок
for row_index, user_info in enumerate(table_content): for row_index, user_info in enumerate(table_content):
if name in user_info and role in user_info: if name in user_info and role in user_info:
return row_index return row_index
@ -216,14 +215,14 @@ class UsersTab(BasePage):
def open_add_user_window(self) -> None: def open_add_user_window(self) -> None:
"""Открывает окно добавления пользователя. """Открывает окно добавления пользователя.
Raises: Raises:
AssertionError: Если кнопки недоступны или окно не открылось. AssertionError: Если кнопки недоступны или окно не открылось.
""" """
if self.toolbar.is_button_not_present("close"): if self.toolbar.is_button_not_present("close"):
self.toolbar.check_button_presence("edit") self.toolbar.check_button_presence("edit")
self.toolbar.click_button("edit") self.toolbar.click_button("edit")
self.toolbar.check_button_presence("add_user") self.toolbar.check_button_presence("add_user")
self.toolbar.click_button("add_user") self.toolbar.click_button("add_user")
self.add_modal_window("add_user", "") self.add_modal_window("add_user", "")
@ -243,26 +242,26 @@ class UsersTab(BasePage):
""" """
tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"} tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"}
table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
if len(table_content) == 0: 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] # Удаляем заголовок del table_content[0] # Удаляем заголовок
if row_index >= len(table_content): if row_index >= len(table_content):
assert False, "Row_index is out of range" assert False, "Row_index is out of range"
user_name = table_content[row_index][0] user_name = table_content[row_index][0]
for key, val in tmp_dict.items(): for key, val in tmp_dict.items():
if user_name == val: if user_name == val:
user_name = key user_name = key
role = table_content[row_index][1] role = table_content[row_index][2]
self.page.locator(TableLocators.TABLE_WORK_AREA).locator("//tbody/tr").nth(row_index).click() self.page.locator(TableLocators.TABLE_WORK_AREA).locator("//tbody/tr").nth(row_index).click()
self.add_modal_window("edit_user", user_name) self.add_modal_window("edit_user", user_name)
self.get_modal_window(user_name).check_by_window_title() self.get_modal_window(user_name).check_by_window_title()
return user_name, role return user_name, role
def open_edit_user_page_by_user(self, user_name: str, role: str) -> None: def open_edit_user_page_by_user(self, user_name: str, role: str) -> None:
@ -275,10 +274,10 @@ class UsersTab(BasePage):
Raises: Raises:
AssertionError: Если пользователь не найден. 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: if row_index == -1:
assert False, f"User with name {user_name} and role {role} has not been found" 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.page.locator(TableLocators.TABLE_WORK_AREA).locator("//tbody/tr").nth(row_index).click()
self.add_modal_window("edit_user", user_name) self.add_modal_window("edit_user", user_name)
self.get_modal_window(user_name).check_by_window_title() self.get_modal_window(user_name).check_by_window_title()
@ -292,22 +291,22 @@ class UsersTab(BasePage):
Raises: Raises:
AssertionError: Если таблица пуста или заголовки не соответствуют. AssertionError: Если таблица пуста или заголовки не соответствуют.
""" """
expected_headers = ['Имя пользователя', 'Роль', 'E-mail', 'Номер для СМС'] expected_headers = ['Имя пользователя', 'Тип авторизации', 'Роль', 'E-mail', 'Номер для СМС']
table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA) table_content = self.users_table.read(TableLocators.TABLE_WORK_AREA)
if len(table_content) == 0: if len(table_content) == 0:
assert False, "The contents of the table are missing" assert False, "The contents of the table are missing"
actual_headers = table_content[0] actual_headers = table_content[0]
self.check_equals( self.check_equals(
actual_headers, actual_headers,
expected_headers, expected_headers,
f"Expected table headers {expected_headers} are not equal {actual_headers}" f"Expected table headers {expected_headers} are not equal {actual_headers}"
) )
if len(table_content) == 1: if len(table_content) == 1:
assert False, "Table body is missing" assert False, "Table body is missing"
if verify: if verify:
self.verify_users_table_content(table_content) self.verify_users_table_content(table_content)
@ -322,44 +321,44 @@ class UsersTab(BasePage):
user_name (str): Имя пользователя user_name (str): Имя пользователя
role (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) edit_user_window.check_content(user_name, role)
def should_be_toolbar(self) -> None: def should_be_toolbar(self) -> None:
"""Проверяет наличие тулбара. """Проверяет наличие тулбара.
Raises: Raises:
AssertionError: Если тулбар или кнопка редактирования отсутствуют. AssertionError: Если тулбар или кнопка редактирования отсутствуют.
""" """
self.toolbar.check_presence("Toolbar is missing") self.toolbar.check_toolbar_presence("Toolbar is missing")
self.toolbar.check_button_presence("edit") self.toolbar.check_button_presence("edit")
def should_be_toolbar_buttons(self) -> None: def should_be_toolbar_buttons(self) -> None:
"""Проверяет наличие и функциональность кнопок тулбара. """Проверяет наличие и функциональность кнопок тулбара.
Raises: Raises:
AssertionError: Если кнопки недоступны или имеют некорректные подсказки. AssertionError: Если кнопки недоступны или имеют некорректные подсказки.
""" """
self.toolbar.check_button_presence("edit") self.toolbar.check_button_presence("edit")
self.toolbar.check_button_tooltip("edit", "Редактировать") self.toolbar.check_button_tooltip("edit", "Редактировать")
self.toolbar.get_button_by_name("edit").click() self.toolbar.get_button_by_name("edit").click()
self.toolbar.check_button_presence("add_user") 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("add_user", "Добавить")
self.toolbar.check_button_tooltip("close", "Закрыть") self.toolbar.check_button_tooltip("close", "Закрыть")
self.toolbar.get_button_by_name("close").click() self.toolbar.get_button_by_name("close").click()
self.toolbar.check_button_presence("edit") self.toolbar.check_button_presence("edit")
def should_be_users_table(self) -> None: def should_be_users_table(self) -> None:
"""Проверяет наличие таблицы пользователей. """Проверяет наличие таблицы пользователей.
Raises: Raises:
AssertionError: Если таблица отсутствует. AssertionError: Если таблица отсутствует.
""" """
self.users_table.check_presence( self.users_table.check_presence(
TableLocators.TABLE_WORK_AREA, TableLocators.TABLE_WORK_AREA,
"Users table is missing" "Users table is missing"
) )
@ -373,7 +372,7 @@ class UsersTab(BasePage):
Raises: Raises:
AssertionError: Если пользователь не найден. AssertionError: Если пользователь не найден.
""" """
found = self.find_user_in_table(name, role) found = self.find_user_in_table(name, role)
if found == -1: if found == -1:
assert False, f"User with name {name} and role {role} has not been found" assert False, f"User with name {name} and role {role} has not been found"
@ -387,7 +386,7 @@ class UsersTab(BasePage):
Raises: Raises:
AssertionError: Если пользователь найден. AssertionError: Если пользователь найден.
""" """
found = self.find_user_in_table(name, role) found = self.find_user_in_table(name, role)
if found != -1: if found != -1:
assert False, f"User with name {name} and role {role} has been found" assert False, f"User with name {name} and role {role} has been found"
@ -402,26 +401,31 @@ class UsersTab(BasePage):
""" """
expected_users_list = [] expected_users_list = []
tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"} tmp_dict = {"admin": "Администратор", "manager": "Контактное лицо", "operator": "Оператор"}
query = { query = {
"id": ["/catalogs/user"], "id": ["/catalogs/user"],
"data": { "data": {
"namePath": True, "namePath": True,
"children": {"flatten": True} "children": {"flatten": True}
} }
} }
response = self.send_post_api_request("e-cmdb/api/query", query) response = self.send_post_api_request("e-cmdb/api/query", query)
response_body = self.get_response_body(response) response_body = self.get_response_body(response)
for item in response_body[0]["children"]: for item in response_body[0]["children"]:
user_info = [] user_info = []
user_name = item["name"] user_name = item["name"]
if user_name in tmp_dict.keys(): if user_name in tmp_dict.keys():
item["name"] = tmp_dict[user_name] item["name"] = tmp_dict[user_name]
user_info.append(item["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: if item["role"] is not None:
role = item["role"] role = item["role"]
if role in roles_dict.keys(): if role in roles_dict.keys():
@ -429,23 +433,23 @@ class UsersTab(BasePage):
user_info.append(item["role"]) user_info.append(item["role"])
else: else:
user_info.append("") user_info.append("")
if item["email"] is not None: if item["email"] is not None:
user_info.append(item["email"]) user_info.append(item["email"])
else: else:
user_info.append("") user_info.append("")
if item["sms_phone"] is not None: if item["sms_phone"] is not None:
user_info.append(item["sms_phone"]) user_info.append(item["sms_phone"])
else: else:
user_info.append("") user_info.append("")
expected_users_list.append(user_info) expected_users_list.append(user_info)
del users_table[0] # Удаляем заголовок del users_table[0] # Удаляем заголовок
self.check_lists_equals( self.check_lists_equals(
users_table, users_table,
expected_users_list, expected_users_list,
"Actual users list is not equal expected users list on base db" "Actual users list is not equal expected users list on base db"
) )

View File

@ -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 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: class TestJsonContainer:
@ -13,7 +13,7 @@ class TestJsonContainer:
"""Фикстура для настройки тестового окружения.""" """Фикстура для настройки тестового окружения."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
@ -26,9 +26,9 @@ class TestJsonContainer:
is_scrollable = lt.check_json_container_verticall_scrolling() is_scrollable = lt.check_json_container_verticall_scrolling()
assert is_scrollable, "Should be verticall scrolling" assert is_scrollable, "Should be verticall scrolling"
lt.scroll_json_container_down() lt.scroll_json_container_down()
lt.wait_for_timeout(3000) lt.wait_for_timeout(3000)
lt.scroll_json_container_up() lt.scroll_json_container_up()
lt.wait_for_timeout(2000) lt.wait_for_timeout(2000)

View File

@ -1,58 +1,57 @@
from pages.login_page import LoginPage
from pages.main_page import MainPage from pages.main_page import MainPage
from pages.login_page import LoginPage
# Запуск с viewport: {'width': 300, 'height': 420} # Запуск с viewport: {'width': 300, 'height': 420}
# @pytest.mark.smoke # @pytest.mark.smoke
class TestNavigationPanel: class TestNavigationPanel:
"""Класс тестов для проверки панели навигации. """Класс тестов для проверки панели навигации.
Атрибуты: Атрибуты:
browser: фикстура для работы с браузером browser: фикстура для работы с браузером
""" """
def test_verticall_scrolling(self, browser): def test_verticall_scrolling(self, browser):
"""Тест вертикальной прокрутки панели навигации. """Тест вертикальной прокрутки панели навигации.
Аргументы: Аргументы:
browser: фикстура для работы с браузером browser: фикстура для работы с браузером
Возвращает: Возвращает:
None None
Исключения: Исключения:
AssertionError: если панель навигации не поддерживает вертикальную прокрутку AssertionError: если панель навигации не поддерживает вертикальную прокрутку
""" """
# Действия: # Действия:
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
# Мы на главной странице # Мы на главной странице
mp = MainPage(browser) mp = MainPage(browser)
# Проверки: # Проверки:
# Проверяем наличие панели навигации # Проверяем наличие панели навигации
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
# Открываем все пункты панели # Открываем все пункты панели
mp.click_main_navigation_panel_item("Настройки") 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("Обслуживание и диагностика")
mp.click_configuration_navigation_panel_item("Zero Touch Provisioning") mp.click_configuration_navigation_panel_item("Zero Touch Provisioning")
# Проверяем возможность вертикальной прокрутки # Проверяем возможность вертикальной прокрутки
is_scrollable = mp.check_navigation_panel_verticall_scrolling() is_scrollable = mp.check_navigation_panel_verticall_scrolling()
assert is_scrollable, "Should be vertical scrolling" assert is_scrollable, "Should be vertical scrolling"
# Действия: # Действия:
# Прокручиваем вверх и проверяем видимость элемента # Прокручиваем вверх и проверяем видимость элемента
mp.scroll_navigation_panel_up() mp.scroll_navigation_panel_up()
mp.check_navigation_panel_item_visibility("Панель приборов") mp.check_navigation_panel_item_visibility("Панель приборов")
mp.wait_for_timeout(3000) mp.wait_for_timeout(3000)
# Прокручиваем вниз и проверяем видимость элемента # Прокручиваем вниз и проверяем видимость элемента Настройки/ZTP/Шаблоны
mp.scroll_navigation_panel_down() mp.scroll_navigation_panel_down()
mp.check_navigation_panel_item_visibility("Шаблоны") mp.check_navigation_panel_item_visibility("Шаблоны_2")
mp.wait_for_timeout(2000) mp.wait_for_timeout(2000)

View File

@ -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 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: class TestServiceStatusTable:
"""Тесты для проверки таблицы статусов сервисов.""" """Тесты для проверки таблицы статусов сервисов."""
@ -13,7 +12,7 @@ class TestServiceStatusTable:
"""Фикстура для настройки тестового окружения.""" """Фикстура для настройки тестового окружения."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
@ -23,17 +22,17 @@ class TestServiceStatusTable:
def test_scrolling(self, browser: Page) -> None: def test_scrolling(self, browser: Page) -> None:
"""Тест проверки прокрутки таблицы статусов сервисов.""" """Тест проверки прокрутки таблицы статусов сервисов."""
sst = ServiceStatusTab(browser) sst = ServiceStatusTab(browser)
sst.should_be_services_table() sst.should_be_services_table()
sst.check_services_table_content() sst.check_services_table_content()
is_scrollable_vertically = sst.check_services_table_verticall_scrolling() is_scrollable_vertically = sst.check_services_table_verticall_scrolling()
assert is_scrollable_vertically, "Should be vertical scrolling" assert is_scrollable_vertically, "Should be vertical scrolling"
sst.scroll_services_table_down() sst.scroll_services_table_down()
sst.check_services_table_last_row_visibility() sst.check_services_table_last_row_visibility()
sst.wait_for_timeout(3000) sst.wait_for_timeout(3000)
sst.scroll_services_table_up() sst.scroll_services_table_up()
sst.check_services_table_first_row_visibility() sst.check_services_table_first_row_visibility()
sst.wait_for_timeout(2000) sst.wait_for_timeout(2000)

View File

@ -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 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: class TestUsersModalWindow:
"""Тесты для проверки модальных окон работы с пользователями.""" """Тесты для проверки модальных окон работы с пользователями."""
@ -13,7 +12,7 @@ class TestUsersModalWindow:
"""Фикстура для настройки тестового окружения.""" """Фикстура для настройки тестового окружения."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
@ -25,21 +24,21 @@ class TestUsersModalWindow:
ut = UsersTab(browser) 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)
modal_window = ut.get_modal_window(user_name) modal_window = ut.get_modal_window(user_name)
is_scrollable_vertically = modal_window.check_window_vertical_scrolling() is_scrollable_vertically = modal_window.check_window_vertical_scrolling()
assert is_scrollable_vertically, "Should be vertical scrolling" assert is_scrollable_vertically, "Should be vertical scrolling"
modal_window.scroll_window_down() modal_window.scroll_window_down()
modal_window.check_button_presence("close") modal_window.check_button_presence("close")
ut.wait_for_timeout(3000) ut.wait_for_timeout(3000)
modal_window.scroll_window_up() modal_window.scroll_window_up()
modal_window.check_toolbar_button_presence("close") modal_window.check_toolbar_button_presence("close")
ut.wait_for_timeout(3000) ut.wait_for_timeout(3000)
is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling() is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling()
assert is_scrollable_horizontally, "Should be horizontal scrolling" assert is_scrollable_horizontally, "Should be horizontal scrolling"
modal_window.scroll_window_right() modal_window.scroll_window_right()
ut.wait_for_timeout(3000) ut.wait_for_timeout(3000)
modal_window.scroll_window_left() modal_window.scroll_window_left()
@ -50,22 +49,23 @@ class TestUsersModalWindow:
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
modal_window = ut.get_modal_window("add_user") modal_window = ut.get_modal_window("add_user")
is_scrollable_vertically = modal_window.check_window_vertical_scrolling() is_scrollable_vertically = modal_window.check_window_vertical_scrolling()
assert is_scrollable_vertically, "Should be vertical scrolling" assert is_scrollable_vertically, "Should be vertical scrolling"
modal_window.scroll_window_down() modal_window.scroll_window_down()
modal_window.check_button_presence("close") modal_window.check_button_presence("close")
ut.wait_for_timeout(3000) ut.wait_for_timeout(3000)
modal_window.scroll_window_up() modal_window.scroll_window_up()
modal_window.check_toolbar_button_presence("close") modal_window.check_toolbar_button_presence("close")
ut.wait_for_timeout(3000) ut.wait_for_timeout(3000)
is_scrollable_horizontally = modal_window.check_window_horizontal_scrolling() ## Временно закомментарено - для окна добавления пользователя убрали горизонтальный скроллинг - BUG???
assert is_scrollable_horizontally, "Should be horizontal scrolling" # 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_right()
modal_window.scroll_window_left() # ut.wait_for_timeout(3000)
ut.wait_for_timeout(2000) # modal_window.scroll_window_left()
# ut.wait_for_timeout(2000)

View File

@ -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 import uuid
from typing import List 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: class TestLicenseTab:
"""Тесты для вкладки 'Лицензии'.""" """Тесты для вкладки 'Лицензии'."""
@ -15,7 +14,7 @@ class TestLicenseTab:
"""Подготовка тестового окружения.""" """Подготовка тестового окружения."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
@ -26,7 +25,7 @@ class TestLicenseTab:
"""Тест содержимого вкладки 'Лицензии'.""" """Тест содержимого вкладки 'Лицензии'."""
lt = LicenseTab(browser) lt = LicenseTab(browser)
lt.check_content() lt.check_content()
def test_license_tab_input_form_and_check_alert(self, browser: Page) -> None: def test_license_tab_input_form_and_check_alert(self, browser: Page) -> None:
"""Тест формы ввода лицензии и проверки алертов.""" """Тест формы ввода лицензии и проверки алертов."""
def gen_test_data() -> List[str]: def gen_test_data() -> List[str]:
@ -34,24 +33,24 @@ class TestLicenseTab:
data = [] data = []
for i in range(3): for i in range(3):
data.append(uuid.uuid4().hex) data.append(uuid.uuid4().hex)
lowercase_str = uuid.uuid4().hex lowercase_str = uuid.uuid4().hex
data.append(lowercase_str.upper()) data.append(lowercase_str.upper())
data.append(lowercase_str + "fffffffff") data.append(lowercase_str + "fffffffff")
data.append("0") data.append("0")
data.append("000000000000000000000000000000000000000000000000") data.append("000000000000000000000000000000000000000000000000")
data.append("-1") data.append("-1")
return data return data
lt = LicenseTab(browser) lt = LicenseTab(browser)
lt.should_be_empty_input_form() lt.should_be_empty_input_form()
lt.fill_license_input_form("") lt.fill_license_input_form("")
lt.should_be_error_alert_window_with_text("Неверный лицензионный ключ") lt.should_be_error_alert_window_with_text("Неверный лицензионный ключ")
data = gen_test_data() data = gen_test_data()
for data_string in data: for data_string in data:
lt.fill_license_input_form(data_string) lt.fill_license_input_form(data_string)
lt.should_be_error_alert_window_with_text("Ошибка обновления лицензии") lt.should_be_error_alert_window_with_text("Ошибка обновления лицензии")

View File

@ -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 playwright.sync_api import Page
from pages.main_page import MainPage
from pages.login_page import LoginPage
class TestLogin: class TestLogin:
"""Тесты для функционала входа и выхода из системы.""" """Тесты для функционала входа и выхода из системы."""
def test_successful_login(self, browser: Page) -> None: def test_successful_login(self, browser: Page) -> None:
"""Тест успешного входа в систему.""" """Тест успешного входа в систему."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
def test_unsuccessful_login(self, browser: Page) -> None: def test_unsuccessful_login(self, browser: Page) -> None:
"""Тест неудачного входа в систему.""" """Тест неудачного входа в систему."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_unsuccessful_login() lp.do_unsuccessful_login()
def test_successful_login_and_logout(self, browser: Page) -> None: def test_successful_login_and_logout(self, browser: Page) -> None:
"""Тест успешного входа и выхода из системы.""" """Тест успешного входа и выхода из системы."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.do_logout() mp.do_logout()

View File

@ -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 import pytest
from pages.service_status_tab import ServiceStatusTab
from pages.main_page import MainPage
from pages.login_page import LoginPage
class TestServiceStatusTab: class TestServiceStatusTab:
"""Набор тестов для вкладки 'Статус обслуживания'. """Набор тестов для вкладки 'Статус обслуживания'.
@ -20,16 +19,16 @@ class TestServiceStatusTab:
""" """
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
# Переход на главную страницу # Переход на главную страницу
mp = MainPage(browser) mp = MainPage(browser)
# Проверка наличия панели навигации # Проверка наличия панели навигации
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
# Клик по пункту 'Настройки' в главной панели навигации # Клик по пункту 'Настройки' в главной панели навигации
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
# Клик по пункту 'Обслуживание и диагностика' в панели навигации настроек # Клик по пункту 'Обслуживание и диагностика' в панели навигации настроек
mp.click_configuration_navigation_panel_item("Обслуживание и диагностика") mp.click_configuration_navigation_panel_item("Обслуживание и диагностика")
@ -48,13 +47,13 @@ class TestServiceStatusTab:
# Проверка тулбара вкладки # Проверка тулбара вкладки
sst.should_be_toolbar() sst.should_be_toolbar()
# Проверка наличия таблицы статусов сервисов # Проверка наличия таблицы статусов сервисов
sst.should_be_services_table() sst.should_be_services_table()
# Проверка содержимого таблицы сервисов # Проверка содержимого таблицы сервисов
sst.check_services_table_content() sst.check_services_table_content()
def test_service_status_table_row_highlighting(self, browser): def test_service_status_table_row_highlighting(self, browser):
"""Тест выделения строк в таблице сервисов. """Тест выделения строк в таблице сервисов.
@ -67,14 +66,14 @@ class TestServiceStatusTab:
# Проверка тулбара вкладки # Проверка тулбара вкладки
sst.should_be_toolbar() sst.should_be_toolbar()
# Проверка наличия таблицы статусов сервисов # Проверка наличия таблицы статусов сервисов
sst.should_be_services_table() sst.should_be_services_table()
# Получение количества строк в таблице # Получение количества строк в таблице
rows_count = sst.get_rows_count() rows_count = sst.get_rows_count()
# Проверка выделения строк # Проверка выделения строк
sst.check_services_table_row_highlighting(0) sst.check_services_table_row_highlighting(0)
sst.check_services_table_row_highlighting(rows_count - 1) 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))

View File

@ -1,8 +1,7 @@
from pages.login_page import LoginPage
from pages.main_page import MainPage
from pages.session_tab import SessionsTab
import pytest import pytest
from pages.session_tab import SessionsTab
from pages.main_page import MainPage
from pages.login_page import LoginPage
class TestSessionsTab: class TestSessionsTab:
"""Набор тестов для вкладки 'Сеансы'. """Набор тестов для вкладки 'Сеансы'.
@ -21,10 +20,10 @@ class TestSessionsTab:
# Авторизация в системе # Авторизация в системе
login_page = LoginPage(browser) login_page = LoginPage(browser)
login_page.do_login() login_page.do_login()
# Инициализация главной страницы # Инициализация главной страницы
main_page = MainPage(browser) main_page = MainPage(browser)
# Проверка и взаимодействие с элементами навигации # Проверка и взаимодействие с элементами навигации
main_page.should_be_navigation_panel() main_page.should_be_navigation_panel()
main_page.click_main_navigation_panel_item("Настройки") main_page.click_main_navigation_panel_item("Настройки")
@ -45,6 +44,6 @@ class TestSessionsTab:
# Проверка элементов интерфейса # Проверка элементов интерфейса
sessions_tab.should_be_toolbar() sessions_tab.should_be_toolbar()
sessions_tab.should_be_sessions_table() sessions_tab.should_be_sessions_table()
# Проверка содержимого таблицы с верификацией данных из БД # Проверка содержимого таблицы с верификацией данных из БД
sessions_tab.check_sessions_table_content(verify=True) sessions_tab.check_sessions_table_content(verify=True)

View File

@ -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 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: class TestUsersTab:
"""Тесты для вкладки 'Пользователи'.""" """Тесты для вкладки 'Пользователи'."""
@ -14,7 +13,7 @@ class TestUsersTab:
"""Подготовка тестового окружения.""" """Подготовка тестового окружения."""
lp = LoginPage(browser) lp = LoginPage(browser)
lp.do_login() lp.do_login()
mp = MainPage(browser) mp = MainPage(browser)
mp.should_be_navigation_panel() mp.should_be_navigation_panel()
mp.click_main_navigation_panel_item("Настройки") mp.click_main_navigation_panel_item("Настройки")
@ -26,7 +25,7 @@ class TestUsersTab:
ut.should_be_toolbar() ut.should_be_toolbar()
ut.should_be_users_table() ut.should_be_users_table()
ut.check_users_table_content(True) ut.check_users_table_content(True)
def test_users_tab_toolbar_buttons(self, browser: Page) -> None: def test_users_tab_toolbar_buttons(self, browser: Page) -> None:
"""Тест кнопок на панели инструментов.""" """Тест кнопок на панели инструментов."""
ut = UsersTab(browser) ut = UsersTab(browser)
@ -37,12 +36,13 @@ class TestUsersTab:
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
ut.check_add_user_window_content() ut.check_add_user_window_content()
def test_add_user_window_close_buttons(self, browser: Page) -> None: def test_add_user_window_close_buttons(self, browser: Page) -> None:
"""Тест кнопок закрытия окна добавления пользователя.""" """Тест кнопок закрытия окна добавления пользователя."""
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
ut.close_add_user_window_by_toolbar_button() ut.close_add_user_window_by_toolbar_button()
ut.open_add_user_window() ut.open_add_user_window()
ut.close_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: def test_edit_user_window_close_buttons(self, browser: Page) -> None:
"""Тест кнопок закрытия окна редактирования пользователя.""" """Тест кнопок закрытия окна редактирования пользователя."""
ut = UsersTab(browser) 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) 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) ut.close_edit_user_window(user_name)
def test_add_and_delete_user(self, browser: Page) -> None: 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) mp = MainPage(browser)
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
ut.add_new_user(user_data) ut.add_new_user(user_data)
mp.click_configuration_navigation_panel_item("Пользователи") mp.click_configuration_navigation_panel_item("Пользователи")
@ -80,21 +80,21 @@ class TestUsersTab:
def test_reset_password(self, browser: Page) -> None: 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) mp = MainPage(browser)
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
ut.add_new_user(user_data) ut.add_new_user(user_data)
mp.click_configuration_navigation_panel_item("Пользователи") mp.click_configuration_navigation_panel_item("Пользователи")
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"]) ut.open_edit_user_page_by_user(user_data["name"], user_data["role"])
new_password = ut.reset_password(user_data["name"]) new_password = ut.reset_password(user_data["name"])
if len(new_password) == 0: if len(new_password) == 0:
assert False, "Unsuccessful password reset" assert False, "Unsuccessful password reset"
new_lp = LoginPage(browser) new_lp = LoginPage(browser)
new_lp.do_login(username=user_data["name"], password=new_password) new_lp.do_login(username=user_data["name"], password=new_password)
new_mp = MainPage(browser) new_mp = MainPage(browser)
@ -111,14 +111,14 @@ class TestUsersTab:
mp_1.click_configuration_navigation_panel_item("Пользователи") mp_1.click_configuration_navigation_panel_item("Пользователи")
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"]) ut_1.should_not_be_user_in_table(user_data["name"], user_data["role"])
def test_edit_user_role(self, browser: Page) -> None: 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) mp = MainPage(browser)
ut = UsersTab(browser) ut = UsersTab(browser)
ut.open_add_user_window() ut.open_add_user_window()
ut.add_new_user(user_data) ut.add_new_user(user_data)
mp.click_configuration_navigation_panel_item("Пользователи") mp.click_configuration_navigation_panel_item("Пользователи")
@ -133,4 +133,4 @@ class TestUsersTab:
ut.delete_user(user_data["name"]) ut.delete_user(user_data["name"])
mp.click_configuration_navigation_panel_item("Пользователи") mp.click_configuration_navigation_panel_item("Пользователи")
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"])

View File

@ -25,12 +25,12 @@ INIT_TEMPLATE: str = """# Auto-generated by fix_python_project.py
class ProjectFixer: class ProjectFixer:
"""Основной класс для исправления структуры Python-проекта. """Основной класс для исправления структуры Python-проекта.
Атрибуты: Атрибуты:
root_dir (str): Корневая директория проекта. root_dir (str): Корневая директория проекта.
log (List[str]): Список записей лога выполненных операций. log (List[str]): Список записей лога выполненных операций.
""" """
def __init__(self, root_dir: str = '.'): def __init__(self, root_dir: str = '.'):
"""Инициализирует экземпляр ProjectFixer. """Инициализирует экземпляр ProjectFixer.
@ -42,7 +42,7 @@ class ProjectFixer:
def remove_bom(self, filepath: str) -> bool: def remove_bom(self, filepath: str) -> bool:
"""Удаляет BOM-маркер из файла, если он присутствует. """Удаляет BOM-маркер из файла, если он присутствует.
Обрабатывает все файлы, включая находящиеся в tests/. Обрабатывает все файлы, включая находящиеся в tests/.
Args: Args:
@ -57,7 +57,7 @@ class ProjectFixer:
try: try:
with open(filepath, 'rb') as f: with open(filepath, 'rb') as f:
content = f.read() content = f.read()
if content.startswith(b'\xEF\xBB\xBF'): if content.startswith(b'\xEF\xBB\xBF'):
with open(filepath, 'wb') as f: with open(filepath, 'wb') as f:
f.write(content[3:]) f.write(content[3:])
@ -69,7 +69,7 @@ class ProjectFixer:
def should_skip_init(self, dir_path: str) -> bool: def should_skip_init(self, dir_path: str) -> bool:
"""Проверяет, нужно ли пропустить создание __init__.py в директории. """Проверяет, нужно ли пропустить создание __init__.py в директории.
Игнорирует служебные папки (tests/, .git/ и др.). Игнорирует служебные папки (tests/, .git/ и др.).
Args: Args:
@ -79,7 +79,7 @@ class ProjectFixer:
bool: True, если директорию следует пропустить. bool: True, если директорию следует пропустить.
""" """
dir_name = os.path.basename(dir_path) 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('.')) dir_name.startswith('.'))
def needs_init_py(self, dir_path: str) -> bool: def needs_init_py(self, dir_path: str) -> bool:
@ -93,7 +93,7 @@ class ProjectFixer:
""" """
if self.should_skip_init(dir_path): if self.should_skip_init(dir_path):
return False return False
try: try:
items = os.listdir(dir_path) items = os.listdir(dir_path)
has_py_files = any(f.endswith('.py') and f != '__init__.py' for f in items) has_py_files = any(f.endswith('.py') and f != '__init__.py' for f in items)
@ -149,14 +149,14 @@ class ProjectFixer:
if __name__ == '__main__': if __name__ == '__main__':
# Обработка аргументов командной строки # Обработка аргументов командной строки
target_dir = sys.argv[1] if len(sys.argv) > 1 else '.' target_dir = sys.argv[1] if len(sys.argv) > 1 else '.'
# Инициализация и запуск обработки # Инициализация и запуск обработки
fixer = ProjectFixer(target_dir) fixer = ProjectFixer(target_dir)
print(f"Исправление структуры проекта в: {fixer.root_dir}") print(f"Исправление структуры проекта в: {fixer.root_dir}")
fixer.process_directory() fixer.process_directory()
fixer.save_log() fixer.save_log()
# Вывод результатов # Вывод результатов
print(f"Готово! Внесено {len(fixer.log)} изменений.") print(f"Готово! Внесено {len(fixer.log)} изменений.")
print(f"Подробности сохранены в project_fix.log") print("Подробности сохранены в project_fix.log")

View File

@ -34,4 +34,4 @@ def get_logger(name: str) -> logging.Logger:
logger.addHandler(handler) logger.addHandler(handler)
return logger return logger